Merge branch 'master' into linux_windowing

This commit is contained in:
Triang3l
2021-08-26 22:58:14 +03:00
813 changed files with 429062 additions and 250262 deletions

View File

@@ -26,6 +26,7 @@ Blitter::~Blitter() { Shutdown(); }
VkResult Blitter::Initialize(VulkanDevice* device) {
device_ = device;
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status = VK_SUCCESS;
// Shaders
@@ -34,8 +35,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_create_info.codeSize = sizeof(blit_vert);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_vert);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_vertex_);
status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_vertex_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
@@ -46,8 +47,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
shader_create_info.codeSize = sizeof(blit_color_frag);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_color_frag);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_color_);
status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_color_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
@@ -58,8 +59,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
shader_create_info.codeSize = sizeof(blit_depth_frag);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_depth_frag);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_depth_);
status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_depth_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
@@ -83,8 +84,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_binding.pImmutableSamplers = nullptr;
texture_set_layout_info.pBindings = &texture_binding;
status = vkCreateDescriptorSetLayout(*device_, &texture_set_layout_info,
nullptr, &descriptor_set_layout_);
status = dfn.vkCreateDescriptorSetLayout(*device_, &texture_set_layout_info,
nullptr, &descriptor_set_layout_);
CheckResult(status, "vkCreateDescriptorSetLayout");
if (status != VK_SUCCESS) {
return status;
@@ -119,8 +120,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
pipeline_layout_info.pushConstantRangeCount =
static_cast<uint32_t>(xe::countof(push_constant_ranges));
pipeline_layout_info.pPushConstantRanges = push_constant_ranges;
status = vkCreatePipelineLayout(*device_, &pipeline_layout_info, nullptr,
&pipeline_layout_);
status = dfn.vkCreatePipelineLayout(*device_, &pipeline_layout_info, nullptr,
&pipeline_layout_);
CheckResult(status, "vkCreatePipelineLayout");
if (status != VK_SUCCESS) {
return status;
@@ -147,8 +148,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
VK_FALSE,
};
status =
vkCreateSampler(*device_, &sampler_create_info, nullptr, &samp_nearest_);
status = dfn.vkCreateSampler(*device_, &sampler_create_info, nullptr,
&samp_nearest_);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -157,8 +158,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
sampler_create_info.minFilter = VK_FILTER_LINEAR;
sampler_create_info.magFilter = VK_FILTER_LINEAR;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
status =
vkCreateSampler(*device_, &sampler_create_info, nullptr, &samp_linear_);
status = dfn.vkCreateSampler(*device_, &sampler_create_info, nullptr,
&samp_linear_);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -168,49 +169,50 @@ VkResult Blitter::Initialize(VulkanDevice* device) {
}
void Blitter::Shutdown() {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
if (samp_nearest_) {
vkDestroySampler(*device_, samp_nearest_, nullptr);
dfn.vkDestroySampler(*device_, samp_nearest_, nullptr);
samp_nearest_ = nullptr;
}
if (samp_linear_) {
vkDestroySampler(*device_, samp_linear_, nullptr);
dfn.vkDestroySampler(*device_, samp_linear_, nullptr);
samp_linear_ = nullptr;
}
if (blit_vertex_) {
vkDestroyShaderModule(*device_, blit_vertex_, nullptr);
dfn.vkDestroyShaderModule(*device_, blit_vertex_, nullptr);
blit_vertex_ = nullptr;
}
if (blit_color_) {
vkDestroyShaderModule(*device_, blit_color_, nullptr);
dfn.vkDestroyShaderModule(*device_, blit_color_, nullptr);
blit_color_ = nullptr;
}
if (blit_depth_) {
vkDestroyShaderModule(*device_, blit_depth_, nullptr);
dfn.vkDestroyShaderModule(*device_, blit_depth_, nullptr);
blit_depth_ = nullptr;
}
if (pipeline_color_) {
vkDestroyPipeline(*device_, pipeline_color_, nullptr);
dfn.vkDestroyPipeline(*device_, pipeline_color_, nullptr);
pipeline_color_ = nullptr;
}
if (pipeline_depth_) {
vkDestroyPipeline(*device_, pipeline_depth_, nullptr);
dfn.vkDestroyPipeline(*device_, pipeline_depth_, nullptr);
pipeline_depth_ = nullptr;
}
if (pipeline_layout_) {
vkDestroyPipelineLayout(*device_, pipeline_layout_, nullptr);
dfn.vkDestroyPipelineLayout(*device_, pipeline_layout_, nullptr);
pipeline_layout_ = nullptr;
}
if (descriptor_set_layout_) {
vkDestroyDescriptorSetLayout(*device_, descriptor_set_layout_, nullptr);
dfn.vkDestroyDescriptorSetLayout(*device_, descriptor_set_layout_, nullptr);
descriptor_set_layout_ = nullptr;
}
for (auto& pipeline : pipelines_) {
vkDestroyPipeline(*device_, pipeline.second, nullptr);
dfn.vkDestroyPipeline(*device_, pipeline.second, nullptr);
}
pipelines_.clear();
for (auto& pass : render_passes_) {
vkDestroyRenderPass(*device_, pass.second, nullptr);
dfn.vkDestroyRenderPass(*device_, pass.second, nullptr);
}
render_passes_.clear();
}
@@ -230,6 +232,8 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
VkFramebuffer dst_framebuffer, VkViewport viewport,
VkRect2D scissor, VkFilter filter,
bool color_or_depth, bool swap_channels) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
// Do we need a full draw, or can we cheap out with a blit command?
bool full_draw = swap_channels || true;
if (full_draw) {
@@ -249,18 +253,18 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
nullptr,
};
vkCmdBeginRenderPass(command_buffer, &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
dfn.vkCmdBeginRenderPass(command_buffer, &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
dfn.vkCmdSetViewport(command_buffer, 0, 1, &viewport);
dfn.vkCmdSetScissor(command_buffer, 0, 1, &scissor);
// Acquire a pipeline.
auto pipeline =
GetPipeline(render_pass, color_or_depth ? blit_color_ : blit_depth_,
color_or_depth);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline);
dfn.vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline);
// Acquire and update a descriptor set for this image.
auto set = descriptor_pool_->AcquireEntry(descriptor_set_layout_);
@@ -287,10 +291,10 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
write.pImageInfo = &image;
write.pBufferInfo = nullptr;
write.pTexelBufferView = nullptr;
vkUpdateDescriptorSets(*device_, 1, &write, 0, nullptr);
dfn.vkUpdateDescriptorSets(*device_, 1, &write, 0, nullptr);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout_, 0, 1, &set, 0, nullptr);
dfn.vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout_, 0, 1, &set, 0, nullptr);
VtxPushConstants vtx_constants = {
{
@@ -306,9 +310,9 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
float(dst_rect.extent.height) / dst_extents.height,
},
};
vkCmdPushConstants(command_buffer, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VtxPushConstants),
&vtx_constants);
dfn.vkCmdPushConstants(command_buffer, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(VtxPushConstants), &vtx_constants);
PixPushConstants pix_constants = {
0,
@@ -316,12 +320,12 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
0,
swap_channels ? 1 : 0,
};
vkCmdPushConstants(command_buffer, pipeline_layout_,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(VtxPushConstants),
sizeof(PixPushConstants), &pix_constants);
dfn.vkCmdPushConstants(
command_buffer, pipeline_layout_, VK_SHADER_STAGE_FRAGMENT_BIT,
sizeof(VtxPushConstants), sizeof(PixPushConstants), &pix_constants);
vkCmdDraw(command_buffer, 4, 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
dfn.vkCmdDraw(command_buffer, 4, 1, 0, 0);
dfn.vkCmdEndRenderPass(command_buffer);
}
}
@@ -421,8 +425,9 @@ VkRenderPass Blitter::CreateRenderPass(VkFormat output_format,
nullptr,
};
VkRenderPass renderpass = nullptr;
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult result =
vkCreateRenderPass(*device_, &renderpass_info, nullptr, &renderpass);
dfn.vkCreateRenderPass(*device_, &renderpass_info, nullptr, &renderpass);
CheckResult(result, "vkCreateRenderPass");
return renderpass;
@@ -431,6 +436,7 @@ VkRenderPass Blitter::CreateRenderPass(VkFormat output_format,
VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass,
VkShaderModule frag_shader,
bool color_or_depth) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult result = VK_SUCCESS;
// Pipeline
@@ -576,8 +582,8 @@ VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass,
pipeline_info.basePipelineIndex = -1;
VkPipeline pipeline = nullptr;
result = vkCreateGraphicsPipelines(*device_, nullptr, 1, &pipeline_info,
nullptr, &pipeline);
result = dfn.vkCreateGraphicsPipelines(*device_, nullptr, 1, &pipeline_info,
nullptr, &pipeline);
CheckResult(result, "vkCreateGraphicsPipelines");
return pipeline;

View File

@@ -22,6 +22,7 @@ namespace vulkan {
CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
VkDeviceSize capacity, VkDeviceSize alignment)
: device_(device), capacity_(capacity) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status = VK_SUCCESS;
// Create our internal buffer.
@@ -34,14 +35,14 @@ CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.queueFamilyIndexCount = 0;
buffer_info.pQueueFamilyIndices = nullptr;
status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
status = dfn.vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
CheckResult(status, "vkCreateBuffer");
if (status != VK_SUCCESS) {
assert_always();
}
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
alignment_ = xe::round_up(alignment, reqs.alignment);
}
CircularBuffer::~CircularBuffer() { Shutdown(); }
@@ -52,10 +53,12 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory,
gpu_memory_ = memory;
gpu_base_ = offset;
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status = VK_SUCCESS;
// Bind the buffer to its backing memory.
status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
status =
dfn.vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
CheckResult(status, "vkBindBufferMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to bind memory!");
@@ -64,8 +67,8 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory,
}
// Map the memory so we can access it.
status = vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
reinterpret_cast<void**>(&host_base_));
status = dfn.vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
reinterpret_cast<void**>(&host_base_));
CheckResult(status, "vkMapMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to map memory!");
@@ -77,10 +80,11 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory,
}
VkResult CircularBuffer::Initialize() {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status = VK_SUCCESS;
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
// Allocate memory from the device to back the buffer.
owns_gpu_memory_ = true;
@@ -95,7 +99,8 @@ VkResult CircularBuffer::Initialize() {
gpu_base_ = 0;
// Bind the buffer to its backing memory.
status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
status =
dfn.vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
CheckResult(status, "vkBindBufferMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to bind memory!");
@@ -104,8 +109,8 @@ VkResult CircularBuffer::Initialize() {
}
// Map the memory so we can access it.
status = vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
reinterpret_cast<void**>(&host_base_));
status = dfn.vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
reinterpret_cast<void**>(&host_base_));
CheckResult(status, "vkMapMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to map memory!");
@@ -118,22 +123,24 @@ VkResult CircularBuffer::Initialize() {
void CircularBuffer::Shutdown() {
Clear();
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
if (host_base_) {
vkUnmapMemory(*device_, gpu_memory_);
dfn.vkUnmapMemory(*device_, gpu_memory_);
host_base_ = nullptr;
}
if (gpu_buffer_) {
vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
dfn.vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
gpu_buffer_ = nullptr;
}
if (gpu_memory_ && owns_gpu_memory_) {
vkFreeMemory(*device_, gpu_memory_, nullptr);
dfn.vkFreeMemory(*device_, gpu_memory_, nullptr);
gpu_memory_ = nullptr;
}
}
void CircularBuffer::GetBufferMemoryRequirements(VkMemoryRequirements* reqs) {
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs);
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs);
}
bool CircularBuffer::CanAcquire(VkDeviceSize length) {
@@ -224,23 +231,25 @@ CircularBuffer::Allocation* CircularBuffer::Acquire(VkDeviceSize length,
}
void CircularBuffer::Flush(Allocation* allocation) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkMappedMemoryRange range;
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.pNext = nullptr;
range.memory = gpu_memory_;
range.offset = gpu_base_ + allocation->offset;
range.size = allocation->length;
vkFlushMappedMemoryRanges(*device_, 1, &range);
dfn.vkFlushMappedMemoryRanges(*device_, 1, &range);
}
void CircularBuffer::Flush(VkDeviceSize offset, VkDeviceSize length) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkMappedMemoryRange range;
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.pNext = nullptr;
range.memory = gpu_memory_;
range.offset = gpu_base_ + offset;
range.size = length;
vkFlushMappedMemoryRanges(*device_, 1, &range);
dfn.vkFlushMappedMemoryRanges(*device_, 1, &range);
}
void CircularBuffer::Clear() {
@@ -249,12 +258,14 @@ void CircularBuffer::Clear() {
}
void CircularBuffer::Scavenge() {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
// Stash the last signalled fence
VkFence fence = nullptr;
while (!allocations_.empty()) {
Allocation& alloc = allocations_.front();
if (fence != alloc.fence &&
vkGetFenceStatus(*device_, alloc.fence) != VK_SUCCESS) {
dfn.vkGetFenceStatus(*device_, alloc.fence) != VK_SUCCESS) {
// Don't bother freeing following allocations to ensure proper ordering.
break;
}

View File

@@ -19,9 +19,11 @@ namespace vulkan {
using xe::ui::vulkan::CheckResult;
CommandBufferPool::CommandBufferPool(VkDevice device,
CommandBufferPool::CommandBufferPool(const VulkanDevice& device,
uint32_t queue_family_index)
: BaseFencedPool(device) {
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
// Create the pool used for allocating buffers.
// They are marked as transient (short-lived) and cycled frequently.
VkCommandPoolCreateInfo cmd_pool_info;
@@ -31,7 +33,7 @@ CommandBufferPool::CommandBufferPool(VkDevice device,
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
cmd_pool_info.queueFamilyIndex = queue_family_index;
auto err =
vkCreateCommandPool(device_, &cmd_pool_info, nullptr, &command_pool_);
dfn.vkCreateCommandPool(device_, &cmd_pool_info, nullptr, &command_pool_);
CheckResult(err, "vkCreateCommandPool");
// Allocate a bunch of command buffers to start.
@@ -43,8 +45,8 @@ CommandBufferPool::CommandBufferPool(VkDevice device,
command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
command_buffer_info.commandBufferCount = kDefaultCount;
VkCommandBuffer command_buffers[kDefaultCount];
err =
vkAllocateCommandBuffers(device_, &command_buffer_info, command_buffers);
err = dfn.vkAllocateCommandBuffers(device_, &command_buffer_info,
command_buffers);
CheckResult(err, "vkCreateCommandBuffer");
for (size_t i = 0; i < xe::countof(command_buffers); ++i) {
PushEntry(command_buffers[i], nullptr);
@@ -53,7 +55,8 @@ CommandBufferPool::CommandBufferPool(VkDevice device,
CommandBufferPool::~CommandBufferPool() {
FreeAllEntries();
vkDestroyCommandPool(device_, command_pool_, nullptr);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
dfn.vkDestroyCommandPool(device_, command_pool_, nullptr);
command_pool_ = nullptr;
}
@@ -67,17 +70,19 @@ VkCommandBuffer CommandBufferPool::AllocateEntry(void* data) {
VkCommandBufferLevel(reinterpret_cast<uintptr_t>(data));
command_buffer_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
auto err =
vkAllocateCommandBuffers(device_, &command_buffer_info, &command_buffer);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
auto err = dfn.vkAllocateCommandBuffers(device_, &command_buffer_info,
&command_buffer);
CheckResult(err, "vkCreateCommandBuffer");
return command_buffer;
}
void CommandBufferPool::FreeEntry(VkCommandBuffer handle) {
vkFreeCommandBuffers(device_, command_pool_, 1, &handle);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
dfn.vkFreeCommandBuffers(device_, command_pool_, 1, &handle);
}
DescriptorPool::DescriptorPool(VkDevice device, uint32_t max_count,
DescriptorPool::DescriptorPool(const VulkanDevice& device, uint32_t max_count,
std::vector<VkDescriptorPoolSize> pool_sizes)
: BaseFencedPool(device) {
VkDescriptorPoolCreateInfo descriptor_pool_info;
@@ -88,13 +93,15 @@ DescriptorPool::DescriptorPool(VkDevice device, uint32_t max_count,
descriptor_pool_info.maxSets = max_count;
descriptor_pool_info.poolSizeCount = uint32_t(pool_sizes.size());
descriptor_pool_info.pPoolSizes = pool_sizes.data();
auto err = vkCreateDescriptorPool(device, &descriptor_pool_info, nullptr,
&descriptor_pool_);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
auto err = dfn.vkCreateDescriptorPool(device, &descriptor_pool_info, nullptr,
&descriptor_pool_);
CheckResult(err, "vkCreateDescriptorPool");
}
DescriptorPool::~DescriptorPool() {
FreeAllEntries();
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
dfn.vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
descriptor_pool_ = nullptr;
}
@@ -108,15 +115,17 @@ VkDescriptorSet DescriptorPool::AllocateEntry(void* data) {
set_alloc_info.descriptorPool = descriptor_pool_;
set_alloc_info.descriptorSetCount = 1;
set_alloc_info.pSetLayouts = &layout;
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
auto err =
vkAllocateDescriptorSets(device_, &set_alloc_info, &descriptor_set);
dfn.vkAllocateDescriptorSets(device_, &set_alloc_info, &descriptor_set);
CheckResult(err, "vkAllocateDescriptorSets");
return descriptor_set;
}
void DescriptorPool::FreeEntry(VkDescriptorSet handle) {
vkFreeDescriptorSets(device_, descriptor_pool_, 1, &handle);
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
dfn.vkFreeDescriptorSets(device_, descriptor_pool_, 1, &handle);
}
} // namespace vulkan

View File

@@ -14,6 +14,7 @@
#include "xenia/base/assert.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
@@ -28,7 +29,7 @@ namespace vulkan {
template <typename T, typename HANDLE>
class BaseFencedPool {
public:
BaseFencedPool(VkDevice device) : device_(device) {}
BaseFencedPool(const VulkanDevice& device) : device_(device) {}
virtual ~BaseFencedPool() {
// TODO(benvanik): wait on fence until done.
@@ -47,11 +48,12 @@ class BaseFencedPool {
// Checks all pending batches for completion and scavenges their entries.
// This should be called as frequently as reasonable.
void Scavenge() {
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
while (pending_batch_list_head_) {
auto batch = pending_batch_list_head_;
assert_not_null(batch->fence);
VkResult status = vkGetFenceStatus(device_, batch->fence);
VkResult status = dfn.vkGetFenceStatus(device_, batch->fence);
if (status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST) {
// Batch has completed. Reclaim.
pending_batch_list_head_ = batch->next;
@@ -80,6 +82,7 @@ class BaseFencedPool {
VkFence BeginBatch(VkFence fence = nullptr) {
assert_null(open_batch_);
Batch* batch = nullptr;
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
if (free_batch_list_head_) {
// Reuse a batch.
batch = free_batch_list_head_;
@@ -88,10 +91,10 @@ class BaseFencedPool {
if (batch->flags & kBatchOwnsFence && !fence) {
// Reset owned fence.
vkResetFences(device_, 1, &batch->fence);
dfn.vkResetFences(device_, 1, &batch->fence);
} else if ((batch->flags & kBatchOwnsFence) && fence) {
// Transfer owned -> external
vkDestroyFence(device_, batch->fence, nullptr);
dfn.vkDestroyFence(device_, batch->fence, nullptr);
batch->fence = fence;
batch->flags &= ~kBatchOwnsFence;
} else if (!(batch->flags & kBatchOwnsFence) && !fence) {
@@ -100,7 +103,8 @@ class BaseFencedPool {
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
VkResult res = vkCreateFence(device_, &info, nullptr, &batch->fence);
VkResult res =
dfn.vkCreateFence(device_, &info, nullptr, &batch->fence);
if (res != VK_SUCCESS) {
assert_always();
}
@@ -121,7 +125,8 @@ class BaseFencedPool {
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
VkResult res = vkCreateFence(device_, &info, nullptr, &batch->fence);
VkResult res =
dfn.vkCreateFence(device_, &info, nullptr, &batch->fence);
if (res != VK_SUCCESS) {
assert_always();
}
@@ -239,13 +244,14 @@ class BaseFencedPool {
}
void FreeAllEntries() {
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
// Run down free lists.
while (free_batch_list_head_) {
auto batch = free_batch_list_head_;
free_batch_list_head_ = batch->next;
if (batch->flags & kBatchOwnsFence) {
vkDestroyFence(device_, batch->fence, nullptr);
dfn.vkDestroyFence(device_, batch->fence, nullptr);
batch->fence = nullptr;
}
delete batch;
@@ -258,7 +264,7 @@ class BaseFencedPool {
}
}
VkDevice device_ = nullptr;
const VulkanDevice& device_;
private:
struct Entry {
@@ -288,7 +294,7 @@ class CommandBufferPool
public:
typedef BaseFencedPool<CommandBufferPool, VkCommandBuffer> Base;
CommandBufferPool(VkDevice device, uint32_t queue_family_index);
CommandBufferPool(const VulkanDevice& device, uint32_t queue_family_index);
~CommandBufferPool() override;
VkCommandBuffer AcquireEntry(
@@ -308,7 +314,7 @@ class DescriptorPool : public BaseFencedPool<DescriptorPool, VkDescriptorSet> {
public:
typedef BaseFencedPool<DescriptorPool, VkDescriptorSet> Base;
DescriptorPool(VkDevice device, uint32_t max_count,
DescriptorPool(const VulkanDevice& device, uint32_t max_count,
std::vector<VkDescriptorPoolSize> pool_sizes);
~DescriptorPool() override;

View File

@@ -0,0 +1,83 @@
// Vulkan 1.0 core device functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkAllocateCommandBuffers)
XE_UI_VULKAN_FUNCTION(vkAllocateDescriptorSets)
XE_UI_VULKAN_FUNCTION(vkAllocateMemory)
XE_UI_VULKAN_FUNCTION(vkBeginCommandBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdBeginRenderPass)
XE_UI_VULKAN_FUNCTION(vkCmdBindDescriptorSets)
XE_UI_VULKAN_FUNCTION(vkBindBufferMemory)
XE_UI_VULKAN_FUNCTION(vkBindImageMemory)
XE_UI_VULKAN_FUNCTION(vkCmdBindIndexBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdBindPipeline)
XE_UI_VULKAN_FUNCTION(vkCmdBindVertexBuffers)
XE_UI_VULKAN_FUNCTION(vkCmdBlitImage)
XE_UI_VULKAN_FUNCTION(vkCmdClearColorImage)
XE_UI_VULKAN_FUNCTION(vkCmdClearDepthStencilImage)
XE_UI_VULKAN_FUNCTION(vkCmdCopyBufferToImage)
XE_UI_VULKAN_FUNCTION(vkCmdCopyImageToBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdDraw)
XE_UI_VULKAN_FUNCTION(vkCmdDrawIndexed)
XE_UI_VULKAN_FUNCTION(vkCmdEndRenderPass)
XE_UI_VULKAN_FUNCTION(vkCmdExecuteCommands)
XE_UI_VULKAN_FUNCTION(vkCmdFillBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdPipelineBarrier)
XE_UI_VULKAN_FUNCTION(vkCmdPushConstants)
XE_UI_VULKAN_FUNCTION(vkCmdResolveImage)
XE_UI_VULKAN_FUNCTION(vkCmdSetBlendConstants)
XE_UI_VULKAN_FUNCTION(vkCmdSetDepthBias)
XE_UI_VULKAN_FUNCTION(vkCmdSetDepthBounds)
XE_UI_VULKAN_FUNCTION(vkCmdSetLineWidth)
XE_UI_VULKAN_FUNCTION(vkCmdSetScissor)
XE_UI_VULKAN_FUNCTION(vkCmdSetStencilCompareMask)
XE_UI_VULKAN_FUNCTION(vkCmdSetStencilReference)
XE_UI_VULKAN_FUNCTION(vkCmdSetStencilWriteMask)
XE_UI_VULKAN_FUNCTION(vkCmdSetViewport)
XE_UI_VULKAN_FUNCTION(vkCreateBuffer)
XE_UI_VULKAN_FUNCTION(vkCreateCommandPool)
XE_UI_VULKAN_FUNCTION(vkCreateDescriptorPool)
XE_UI_VULKAN_FUNCTION(vkCreateDescriptorSetLayout)
XE_UI_VULKAN_FUNCTION(vkCreateFence)
XE_UI_VULKAN_FUNCTION(vkCreateFramebuffer)
XE_UI_VULKAN_FUNCTION(vkCreateGraphicsPipelines)
XE_UI_VULKAN_FUNCTION(vkCreateImage)
XE_UI_VULKAN_FUNCTION(vkCreateImageView)
XE_UI_VULKAN_FUNCTION(vkCreatePipelineCache)
XE_UI_VULKAN_FUNCTION(vkCreatePipelineLayout)
XE_UI_VULKAN_FUNCTION(vkCreateRenderPass)
XE_UI_VULKAN_FUNCTION(vkCreateSampler)
XE_UI_VULKAN_FUNCTION(vkCreateSemaphore)
XE_UI_VULKAN_FUNCTION(vkCreateShaderModule)
XE_UI_VULKAN_FUNCTION(vkDestroyBuffer)
XE_UI_VULKAN_FUNCTION(vkDestroyCommandPool)
XE_UI_VULKAN_FUNCTION(vkDestroyDescriptorPool)
XE_UI_VULKAN_FUNCTION(vkDestroyDescriptorSetLayout)
XE_UI_VULKAN_FUNCTION(vkDestroyFence)
XE_UI_VULKAN_FUNCTION(vkDestroyFramebuffer)
XE_UI_VULKAN_FUNCTION(vkDestroyImage)
XE_UI_VULKAN_FUNCTION(vkDestroyImageView)
XE_UI_VULKAN_FUNCTION(vkDestroyPipeline)
XE_UI_VULKAN_FUNCTION(vkDestroyPipelineCache)
XE_UI_VULKAN_FUNCTION(vkDestroyPipelineLayout)
XE_UI_VULKAN_FUNCTION(vkDestroyRenderPass)
XE_UI_VULKAN_FUNCTION(vkDestroySampler)
XE_UI_VULKAN_FUNCTION(vkDestroySemaphore)
XE_UI_VULKAN_FUNCTION(vkDestroyShaderModule)
XE_UI_VULKAN_FUNCTION(vkEndCommandBuffer)
XE_UI_VULKAN_FUNCTION(vkFlushMappedMemoryRanges)
XE_UI_VULKAN_FUNCTION(vkFreeCommandBuffers)
XE_UI_VULKAN_FUNCTION(vkFreeDescriptorSets)
XE_UI_VULKAN_FUNCTION(vkFreeMemory)
XE_UI_VULKAN_FUNCTION(vkGetDeviceQueue)
XE_UI_VULKAN_FUNCTION(vkGetBufferMemoryRequirements)
XE_UI_VULKAN_FUNCTION(vkGetFenceStatus)
XE_UI_VULKAN_FUNCTION(vkGetImageMemoryRequirements)
XE_UI_VULKAN_FUNCTION(vkGetImageSubresourceLayout)
XE_UI_VULKAN_FUNCTION(vkGetPipelineCacheData)
XE_UI_VULKAN_FUNCTION(vkMapMemory)
XE_UI_VULKAN_FUNCTION(vkQueueSubmit)
XE_UI_VULKAN_FUNCTION(vkQueueWaitIdle)
XE_UI_VULKAN_FUNCTION(vkResetCommandBuffer)
XE_UI_VULKAN_FUNCTION(vkResetFences)
XE_UI_VULKAN_FUNCTION(vkUnmapMemory)
XE_UI_VULKAN_FUNCTION(vkUpdateDescriptorSets)
XE_UI_VULKAN_FUNCTION(vkWaitForFences)

View File

@@ -0,0 +1,2 @@
// VK_AMD_shader_info functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkGetShaderInfoAMD)

View File

@@ -0,0 +1,5 @@
// VK_EXT_debug_marker functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkDebugMarkerSetObjectNameEXT)
XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerBeginEXT)
XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerEndEXT)
XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerInsertEXT)

View File

@@ -0,0 +1,6 @@
// VK_KHR_swapchain functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkAcquireNextImageKHR)
XE_UI_VULKAN_FUNCTION(vkCreateSwapchainKHR)
XE_UI_VULKAN_FUNCTION(vkDestroySwapchainKHR)
XE_UI_VULKAN_FUNCTION(vkGetSwapchainImagesKHR)
XE_UI_VULKAN_FUNCTION(vkQueuePresentKHR)

View File

@@ -0,0 +1,13 @@
// Vulkan 1.0 core instance functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkCreateDevice)
XE_UI_VULKAN_FUNCTION(vkDestroyDevice)
XE_UI_VULKAN_FUNCTION(vkEnumerateDeviceExtensionProperties)
XE_UI_VULKAN_FUNCTION(vkEnumerateDeviceLayerProperties)
XE_UI_VULKAN_FUNCTION(vkEnumeratePhysicalDevices)
XE_UI_VULKAN_FUNCTION(vkGetDeviceProcAddr)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceFeatures)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceFormatProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceImageFormatProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceMemoryProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties)

View File

@@ -0,0 +1,3 @@
// VK_EXT_debug_report functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkCreateDebugReportCallbackEXT)
XE_UI_VULKAN_FUNCTION(vkDestroyDebugReportCallbackEXT)

View File

@@ -0,0 +1,2 @@
// VK_KHR_android_surface functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkCreateAndroidSurfaceKHR)

View File

@@ -0,0 +1,6 @@
// VK_KHR_surface functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkDestroySurfaceKHR)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR)

View File

@@ -0,0 +1,2 @@
// VK_KHR_win32_surface functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkCreateWin32SurfaceKHR)

View File

@@ -0,0 +1,2 @@
// VK_KHR_xcb_surface functions used in Xenia.
XE_UI_VULKAN_FUNCTION(vkCreateXcbSurfaceKHR)

View File

@@ -18,6 +18,7 @@ project("xenia-ui-vulkan")
project_root.."/third_party/vulkan/",
})
local_platform_files()
local_platform_files("functions")
files({
"shaders/bin/*.h",
})
@@ -31,7 +32,6 @@ project("xenia-ui-window-vulkan-demo")
links({
"fmt",
"imgui",
"volk",
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",

View File

@@ -10,20 +10,30 @@
#ifndef XENIA_UI_VULKAN_VULKAN_H_
#define XENIA_UI_VULKAN_VULKAN_H_
#include "xenia/base/cvar.h"
#include "xenia/base/platform.h"
#if XE_PLATFORM_WIN32
#define VK_USE_PLATFORM_WIN32_KHR 1
#elif XE_PLATFORM_LINUX
#if XE_PLATFORM_ANDROID
#ifndef VK_USE_PLATFORM_ANDROID_KHR
#define VK_USE_PLATFORM_ANDROID_KHR 1
#endif
#elif XE_PLATFORM_GNU_LINUX
#ifndef VK_USE_PLATFORM_XCB_KHR
#define VK_USE_PLATFORM_XCB_KHR 1
#else
#error Platform not yet supported.
#endif // XE_PLATFORM_WIN32
#endif
#elif XE_PLATFORM_WIN32
// Must be included before vulkan.h with VK_USE_PLATFORM_WIN32_KHR because it
// includes Windows.h too.
#include "xenia/base/platform_win.h"
#ifndef VK_USE_PLATFORM_WIN32_KHR
#define VK_USE_PLATFORM_WIN32_KHR 1
#endif
#endif
// We use a loader with its own function prototypes.
#include "third_party/volk/volk.h"
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES 1
#endif
#include "third_party/vulkan/vulkan.h"
#include "xenia/base/cvar.h"
#define XELOGVK XELOGI

View File

@@ -25,7 +25,7 @@
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
#if XE_PLATFORM_LINUX
#if XE_PLATFORM_GNU_LINUX
#include "xenia/ui/window_gtk.h"
#include <X11/Xlib-xcb.h>
@@ -41,10 +41,11 @@ VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
VulkanContext::~VulkanContext() {
VkResult status;
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
VulkanDevice* device = provider->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
{
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
status = vkQueueWaitIdle(device->primary_queue());
status = dfn.vkQueueWaitIdle(device->primary_queue());
}
immediate_drawer_.reset();
swap_chain_.reset();
@@ -52,6 +53,8 @@ VulkanContext::~VulkanContext() {
bool VulkanContext::Initialize() {
auto provider = static_cast<VulkanProvider*>(provider_);
VulkanInstance* instance = provider->instance();
const VulkanInstance::InstanceFunctions& ifn = instance->ifn();
if (target_window_) {
// Create swap chain used to present to the window.
@@ -65,10 +68,10 @@ bool VulkanContext::Initialize() {
create_info.hinstance =
static_cast<HINSTANCE>(target_window_->native_platform_handle());
create_info.hwnd = static_cast<HWND>(target_window_->native_handle());
status = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
nullptr, &surface);
status = ifn.vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
nullptr, &surface);
CheckResult(status, "vkCreateWin32SurfaceKHR");
#elif XE_PLATFORM_LINUX
#elif XE_PLATFORM_GNU_LINUX
#ifdef GDK_WINDOWING_X11
GtkWidget* window_handle =
dynamic_cast<GTKWindow*>(target_window_)->native_window_handle();
@@ -81,8 +84,8 @@ bool VulkanContext::Initialize() {
create_info.connection = static_cast<xcb_connection_t*>(
target_window_->native_platform_handle());
create_info.window = static_cast<xcb_window_t>(window);
status = vkCreateXcbSurfaceKHR(*provider->instance(), &create_info, nullptr,
&surface);
status = ifn.vkCreateXcbSurfaceKHR(*provider->instance(), &create_info,
nullptr, &surface);
CheckResult(status, "vkCreateXcbSurfaceKHR");
#else
#error Unsupported GDK Backend on Linux.
@@ -95,8 +98,8 @@ bool VulkanContext::Initialize() {
return false;
}
swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(),
provider->device());
swap_chain_ =
std::make_unique<VulkanSwapChain>(instance, provider->device());
if (swap_chain_->Initialize(surface) != VK_SUCCESS) {
XELOGE("Unable to initialize swap chain");
return false;
@@ -139,7 +142,8 @@ void VulkanContext::ClearCurrent() {}
bool VulkanContext::BeginSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
VulkanDevice* device = provider->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
VkResult status;
@@ -164,15 +168,16 @@ bool VulkanContext::BeginSwap() {
// TODO(benvanik): use a fence instead? May not be possible with target image.
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
status = vkQueueWaitIdle(device->primary_queue());
status = dfn.vkQueueWaitIdle(device->primary_queue());
return true;
}
void VulkanContext::EndSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
auto provider = static_cast<const VulkanProvider*>(provider_);
VulkanDevice* device = provider->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
VkResult status;
@@ -188,7 +193,7 @@ void VulkanContext::EndSwap() {
// Wait until the queue is idle.
// TODO(benvanik): is this required?
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
status = vkQueueWaitIdle(device->primary_queue());
status = dfn.vkQueueWaitIdle(device->primary_queue());
}
std::unique_ptr<RawImage> VulkanContext::Capture() {

View File

@@ -11,6 +11,7 @@
#include <cinttypes>
#include <climits>
#include <cstring>
#include <mutex>
#include <string>
@@ -68,7 +69,8 @@ VulkanDevice::VulkanDevice(VulkanInstance* instance) : instance_(instance) {
VulkanDevice::~VulkanDevice() {
if (handle) {
vkDestroyDevice(handle, nullptr);
const VulkanInstance::InstanceFunctions& ifn = instance_->ifn();
ifn.vkDestroyDevice(handle, nullptr);
handle = nullptr;
}
}
@@ -91,9 +93,11 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
return false;
}
const VulkanInstance::InstanceFunctions& ifn = instance_->ifn();
// Query supported features so we can make sure we have what we need.
VkPhysicalDeviceFeatures supported_features;
vkGetPhysicalDeviceFeatures(device_info.handle, &supported_features);
ifn.vkGetPhysicalDeviceFeatures(device_info.handle, &supported_features);
VkPhysicalDeviceFeatures enabled_features = {0};
bool any_features_missing = false;
#define ENABLE_AND_EXPECT(name) \
@@ -189,7 +193,8 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
create_info.ppEnabledExtensionNames = enabled_extensions_.data();
create_info.pEnabledFeatures = &enabled_features;
auto err = vkCreateDevice(device_info.handle, &create_info, nullptr, &handle);
auto err =
ifn.vkCreateDevice(device_info.handle, &create_info, nullptr, &handle);
switch (err) {
case VK_SUCCESS:
// Ok!
@@ -211,30 +216,34 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
return false;
}
// Set flags so we can track enabled extensions easily.
for (auto& ext : enabled_extensions_) {
if (!std::strcmp(ext, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
debug_marker_ena_ = true;
pfn_vkDebugMarkerSetObjectNameEXT_ =
(PFN_vkDebugMarkerSetObjectNameEXT)vkGetDeviceProcAddr(
*this, "vkDebugMarkerSetObjectNameEXT");
pfn_vkCmdDebugMarkerBeginEXT_ =
(PFN_vkCmdDebugMarkerBeginEXT)vkGetDeviceProcAddr(
*this, "vkCmdDebugMarkerBeginEXT");
pfn_vkCmdDebugMarkerEndEXT_ =
(PFN_vkCmdDebugMarkerEndEXT)vkGetDeviceProcAddr(
*this, "vkCmdDebugMarkerEndEXT");
pfn_vkCmdDebugMarkerInsertEXT_ =
(PFN_vkCmdDebugMarkerInsertEXT)vkGetDeviceProcAddr(
*this, "vkCmdDebugMarkerInsertEXT");
}
// Get device functions.
std::memset(&dfn_, 0, sizeof(dfn_));
bool device_functions_loaded = true;
debug_marker_ena_ = false;
#define XE_UI_VULKAN_FUNCTION(name) \
device_functions_loaded &= \
(dfn_.name = PFN_##name(ifn.vkGetDeviceProcAddr(handle, #name))) != \
nullptr;
#include "xenia/ui/vulkan/functions/device_1_0.inc"
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
if (HasEnabledExtension(VK_AMD_SHADER_INFO_EXTENSION_NAME)) {
#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc"
}
debug_marker_ena_ = HasEnabledExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
if (debug_marker_ena_) {
#include "xenia/ui/vulkan/functions/device_ext_debug_marker.inc"
}
#undef XE_UI_VULKAN_FUNCTION
if (!device_functions_loaded) {
XELOGE("Failed to get Vulkan device function pointers");
return false;
}
device_info_ = std::move(device_info);
queue_family_index_ = ideal_queue_family_index;
// Get the primary queue used for most submissions/etc.
vkGetDeviceQueue(handle, queue_family_index_, 0, &primary_queue_);
dfn_.vkGetDeviceQueue(handle, queue_family_index_, 0, &primary_queue_);
if (!primary_queue_) {
XELOGE("vkGetDeviceQueue returned nullptr!");
return false;
@@ -253,7 +262,7 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) {
continue;
}
vkGetDeviceQueue(handle, i, j, &queue);
dfn_.vkGetDeviceQueue(handle, i, j, &queue);
if (queue) {
free_queues_[i].push_back(queue);
}
@@ -292,8 +301,8 @@ void VulkanDevice::ReleaseQueue(VkQueue queue, uint32_t queue_family_index) {
void VulkanDevice::DbgSetObjectName(uint64_t object,
VkDebugReportObjectTypeEXT object_type,
std::string name) {
if (!debug_marker_ena_ || pfn_vkDebugMarkerSetObjectNameEXT_ == nullptr) {
const std::string& name) const {
if (!debug_marker_ena_) {
// Extension disabled.
return;
}
@@ -304,13 +313,13 @@ void VulkanDevice::DbgSetObjectName(uint64_t object,
info.objectType = object_type;
info.object = object;
info.pObjectName = name.c_str();
pfn_vkDebugMarkerSetObjectNameEXT_(*this, &info);
dfn_.vkDebugMarkerSetObjectNameEXT(*this, &info);
}
void VulkanDevice::DbgMarkerBegin(VkCommandBuffer command_buffer,
std::string name, float r, float g, float b,
float a) {
if (!debug_marker_ena_ || pfn_vkCmdDebugMarkerBeginEXT_ == nullptr) {
float a) const {
if (!debug_marker_ena_) {
// Extension disabled.
return;
}
@@ -323,22 +332,22 @@ void VulkanDevice::DbgMarkerBegin(VkCommandBuffer command_buffer,
info.color[1] = g;
info.color[2] = b;
info.color[3] = a;
pfn_vkCmdDebugMarkerBeginEXT_(command_buffer, &info);
dfn_.vkCmdDebugMarkerBeginEXT(command_buffer, &info);
}
void VulkanDevice::DbgMarkerEnd(VkCommandBuffer command_buffer) {
if (!debug_marker_ena_ || pfn_vkCmdDebugMarkerEndEXT_ == nullptr) {
void VulkanDevice::DbgMarkerEnd(VkCommandBuffer command_buffer) const {
if (!debug_marker_ena_) {
// Extension disabled.
return;
}
pfn_vkCmdDebugMarkerEndEXT_(command_buffer);
dfn_.vkCmdDebugMarkerEndEXT(command_buffer);
}
void VulkanDevice::DbgMarkerInsert(VkCommandBuffer command_buffer,
std::string name, float r, float g, float b,
float a) {
if (!debug_marker_ena_ || pfn_vkCmdDebugMarkerInsertEXT_ == nullptr) {
float a) const {
if (!debug_marker_ena_) {
// Extension disabled.
return;
}
@@ -351,7 +360,7 @@ void VulkanDevice::DbgMarkerInsert(VkCommandBuffer command_buffer,
info.color[1] = g;
info.color[2] = g;
info.color[3] = b;
pfn_vkCmdDebugMarkerInsertEXT_(command_buffer, &info);
dfn_.vkCmdDebugMarkerInsertEXT(command_buffer, &info);
}
bool VulkanDevice::is_renderdoc_attached() const {
@@ -379,7 +388,8 @@ void VulkanDevice::EndRenderDocFrameCapture() {
}
VkDeviceMemory VulkanDevice::AllocateMemory(
const VkMemoryRequirements& requirements, VkFlags required_properties) {
const VkMemoryRequirements& requirements,
VkFlags required_properties) const {
// Search memory types to find one matching our requirements and our
// properties.
uint32_t type_index = UINT_MAX;
@@ -407,7 +417,7 @@ VkDeviceMemory VulkanDevice::AllocateMemory(
memory_info.allocationSize = requirements.size;
memory_info.memoryTypeIndex = type_index;
VkDeviceMemory memory = nullptr;
auto err = vkAllocateMemory(handle, &memory_info, nullptr, &memory);
auto err = dfn_.vkAllocateMemory(handle, &memory_info, nullptr, &memory);
CheckResult(err, "vkAllocateMemory");
return memory;
}

View File

@@ -32,11 +32,23 @@ class VulkanDevice {
VulkanDevice(VulkanInstance* instance);
~VulkanDevice();
VulkanInstance* instance() const { return instance_; }
VkDevice handle = nullptr;
operator VkDevice() const { return handle; }
operator VkPhysicalDevice() const { return device_info_.handle; }
struct DeviceFunctions {
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name;
#include "xenia/ui/vulkan/functions/device_1_0.inc"
#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc"
#include "xenia/ui/vulkan/functions/device_ext_debug_marker.inc"
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
#undef XE_UI_VULKAN_FUNCTION
};
const DeviceFunctions& dfn() const { return dfn_; }
// Declares a layer to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredLayer(std::string name, uint32_t min_version,
@@ -78,16 +90,16 @@ class VulkanDevice {
void ReleaseQueue(VkQueue queue, uint32_t queue_family_index);
void DbgSetObjectName(uint64_t object, VkDebugReportObjectTypeEXT object_type,
std::string name);
const std::string& name) const;
void DbgMarkerBegin(VkCommandBuffer command_buffer, std::string name,
float r = 0.0f, float g = 0.0f, float b = 0.0f,
float a = 0.0f);
void DbgMarkerEnd(VkCommandBuffer command_buffer);
float a = 0.0f) const;
void DbgMarkerEnd(VkCommandBuffer command_buffer) const;
void DbgMarkerInsert(VkCommandBuffer command_buffer, std::string name,
float r = 0.0f, float g = 0.0f, float b = 0.0f,
float a = 0.0f);
float a = 0.0f) const;
// True if RenderDoc is attached and available for use.
bool is_renderdoc_attached() const;
@@ -101,7 +113,7 @@ class VulkanDevice {
// Allocates memory of the given size matching the required properties.
VkDeviceMemory AllocateMemory(
const VkMemoryRequirements& requirements,
VkFlags required_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VkFlags required_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) const;
private:
VulkanInstance* instance_ = nullptr;
@@ -110,6 +122,8 @@ class VulkanDevice {
std::vector<Requirement> required_extensions_;
std::vector<const char*> enabled_extensions_;
DeviceFunctions dfn_ = {};
bool debug_marker_ena_ = false;
PFN_vkDebugMarkerSetObjectNameEXT pfn_vkDebugMarkerSetObjectNameEXT_ =
nullptr;

View File

@@ -29,9 +29,11 @@ constexpr uint32_t kCircularBufferCapacity = 2 * 1024 * 1024;
class LightweightCircularBuffer {
public:
LightweightCircularBuffer(VulkanDevice* device) : device_(*device) {
LightweightCircularBuffer(const VulkanDevice* device) : device_(*device) {
buffer_capacity_ = xe::round_up(kCircularBufferCapacity, 4096);
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
// Index buffer.
VkBufferCreateInfo index_buffer_info;
index_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@@ -42,8 +44,8 @@ class LightweightCircularBuffer {
index_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
index_buffer_info.queueFamilyIndexCount = 0;
index_buffer_info.pQueueFamilyIndices = nullptr;
auto status =
vkCreateBuffer(device_, &index_buffer_info, nullptr, &index_buffer_);
auto status = dfn.vkCreateBuffer(device_, &index_buffer_info, nullptr,
&index_buffer_);
CheckResult(status, "vkCreateBuffer");
// Vertex buffer.
@@ -56,34 +58,37 @@ class LightweightCircularBuffer {
vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vertex_buffer_info.queueFamilyIndexCount = 0;
vertex_buffer_info.pQueueFamilyIndices = nullptr;
status =
vkCreateBuffer(*device, &vertex_buffer_info, nullptr, &vertex_buffer_);
status = dfn.vkCreateBuffer(*device, &vertex_buffer_info, nullptr,
&vertex_buffer_);
CheckResult(status, "vkCreateBuffer");
// Allocate underlying buffer.
// We alias it for both vertices and indices.
VkMemoryRequirements buffer_requirements;
vkGetBufferMemoryRequirements(device_, index_buffer_, &buffer_requirements);
dfn.vkGetBufferMemoryRequirements(device_, index_buffer_,
&buffer_requirements);
buffer_memory_ = device->AllocateMemory(
buffer_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
vkBindBufferMemory(*device, index_buffer_, buffer_memory_, 0);
vkBindBufferMemory(*device, vertex_buffer_, buffer_memory_, 0);
dfn.vkBindBufferMemory(*device, index_buffer_, buffer_memory_, 0);
dfn.vkBindBufferMemory(*device, vertex_buffer_, buffer_memory_, 0);
// Persistent mapping.
status = vkMapMemory(device_, buffer_memory_, 0, VK_WHOLE_SIZE, 0,
&buffer_data_);
status = dfn.vkMapMemory(device_, buffer_memory_, 0, VK_WHOLE_SIZE, 0,
&buffer_data_);
CheckResult(status, "vkMapMemory");
}
~LightweightCircularBuffer() {
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
if (buffer_memory_) {
vkUnmapMemory(device_, buffer_memory_);
dfn.vkUnmapMemory(device_, buffer_memory_);
buffer_memory_ = nullptr;
}
VK_SAFE_DESTROY(vkDestroyBuffer, device_, index_buffer_, nullptr);
VK_SAFE_DESTROY(vkDestroyBuffer, device_, vertex_buffer_, nullptr);
VK_SAFE_DESTROY(vkFreeMemory, device_, buffer_memory_, nullptr);
DestroyAndNullHandle(dfn.vkDestroyBuffer, device_, index_buffer_);
DestroyAndNullHandle(dfn.vkDestroyBuffer, device_, vertex_buffer_);
DestroyAndNullHandle(dfn.vkFreeMemory, device_, buffer_memory_);
}
VkBuffer vertex_buffer() const { return vertex_buffer_; }
@@ -118,18 +123,19 @@ class LightweightCircularBuffer {
// Flush memory.
// TODO(benvanik): do only in large batches? can barrier it.
const VulkanDevice::DeviceFunctions& dfn = device_.dfn();
VkMappedMemoryRange dirty_range;
dirty_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
dirty_range.pNext = nullptr;
dirty_range.memory = buffer_memory_;
dirty_range.offset = offset;
dirty_range.size = source_length;
vkFlushMappedMemoryRanges(device_, 1, &dirty_range);
dfn.vkFlushMappedMemoryRanges(device_, 1, &dirty_range);
return offset;
}
private:
VkDevice device_ = nullptr;
const VulkanDevice& device_;
VkBuffer index_buffer_ = nullptr;
VkBuffer vertex_buffer_ = nullptr;
@@ -153,6 +159,7 @@ class VulkanImmediateTexture : public ImmediateTexture {
VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout,
VkImageView image_view) {
image_view_ = image_view;
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
// Create descriptor set used just for this texture.
@@ -163,8 +170,8 @@ class VulkanImmediateTexture : public ImmediateTexture {
set_alloc_info.descriptorPool = descriptor_pool_;
set_alloc_info.descriptorSetCount = 1;
set_alloc_info.pSetLayouts = &descriptor_set_layout;
status =
vkAllocateDescriptorSets(*device_, &set_alloc_info, &descriptor_set_);
status = dfn.vkAllocateDescriptorSets(*device_, &set_alloc_info,
&descriptor_set_);
CheckResult(status, "vkAllocateDescriptorSets");
if (status != VK_SUCCESS) {
return status;
@@ -184,12 +191,13 @@ class VulkanImmediateTexture : public ImmediateTexture {
descriptor_write.descriptorCount = 1;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.pImageInfo = &texture_info;
vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr);
dfn.vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr);
return VK_SUCCESS;
}
VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
// Create image object.
@@ -209,7 +217,7 @@ class VulkanImmediateTexture : public ImmediateTexture {
image_info.queueFamilyIndexCount = 0;
image_info.pQueueFamilyIndices = nullptr;
image_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
status = vkCreateImage(*device_, &image_info, nullptr, &image_);
status = dfn.vkCreateImage(*device_, &image_info, nullptr, &image_);
CheckResult(status, "vkCreateImage");
if (status != VK_SUCCESS) {
return status;
@@ -217,7 +225,7 @@ class VulkanImmediateTexture : public ImmediateTexture {
// Allocate memory for the image.
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(*device_, image_, &memory_requirements);
dfn.vkGetImageMemoryRequirements(*device_, image_, &memory_requirements);
device_memory_ = device_->AllocateMemory(
memory_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (!device_memory_) {
@@ -225,7 +233,7 @@ class VulkanImmediateTexture : public ImmediateTexture {
}
// Bind memory and the image together.
status = vkBindImageMemory(*device_, image_, device_memory_, 0);
status = dfn.vkBindImageMemory(*device_, image_, device_memory_, 0);
CheckResult(status, "vkBindImageMemory");
if (status != VK_SUCCESS) {
return status;
@@ -246,7 +254,7 @@ class VulkanImmediateTexture : public ImmediateTexture {
VK_COMPONENT_SWIZZLE_A,
};
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
status = vkCreateImageView(*device_, &view_info, nullptr, &image_view_);
status = dfn.vkCreateImageView(*device_, &view_info, nullptr, &image_view_);
CheckResult(status, "vkCreateImageView");
if (status != VK_SUCCESS) {
return status;
@@ -260,8 +268,8 @@ class VulkanImmediateTexture : public ImmediateTexture {
set_alloc_info.descriptorPool = descriptor_pool_;
set_alloc_info.descriptorSetCount = 1;
set_alloc_info.pSetLayouts = &descriptor_set_layout;
status =
vkAllocateDescriptorSets(*device_, &set_alloc_info, &descriptor_set_);
status = dfn.vkAllocateDescriptorSets(*device_, &set_alloc_info,
&descriptor_set_);
CheckResult(status, "vkAllocateDescriptorSets");
if (status != VK_SUCCESS) {
return status;
@@ -281,44 +289,48 @@ class VulkanImmediateTexture : public ImmediateTexture {
descriptor_write.descriptorCount = 1;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.pImageInfo = &texture_info;
vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr);
dfn.vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr);
return VK_SUCCESS;
}
void Shutdown() {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
if (descriptor_set_) {
vkFreeDescriptorSets(*device_, descriptor_pool_, 1, &descriptor_set_);
dfn.vkFreeDescriptorSets(*device_, descriptor_pool_, 1, &descriptor_set_);
descriptor_set_ = nullptr;
}
VK_SAFE_DESTROY(vkDestroyImageView, *device_, image_view_, nullptr);
VK_SAFE_DESTROY(vkDestroyImage, *device_, image_, nullptr);
VK_SAFE_DESTROY(vkFreeMemory, *device_, device_memory_, nullptr);
DestroyAndNullHandle(dfn.vkDestroyImageView, *device_, image_view_);
DestroyAndNullHandle(dfn.vkDestroyImage, *device_, image_);
DestroyAndNullHandle(dfn.vkFreeMemory, *device_, device_memory_);
}
VkResult Upload(const uint8_t* src_data) {
// TODO(benvanik): assert not in use? textures aren't dynamic right now.
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
// Get device image layout.
VkImageSubresource subresource;
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource.mipLevel = 0;
subresource.arrayLayer = 0;
VkSubresourceLayout layout;
vkGetImageSubresourceLayout(*device_, image_, &subresource, &layout);
dfn.vkGetImageSubresourceLayout(*device_, image_, &subresource, &layout);
// Map memory for upload.
uint8_t* gpu_data = nullptr;
auto status = vkMapMemory(*device_, device_memory_, 0, layout.size, 0,
reinterpret_cast<void**>(&gpu_data));
auto status = dfn.vkMapMemory(*device_, device_memory_, 0, layout.size, 0,
reinterpret_cast<void**>(&gpu_data));
CheckResult(status, "vkMapMemory");
if (status == VK_SUCCESS) {
// Copy the entire texture, hoping its layout matches what we expect.
std::memcpy(gpu_data + layout.offset, src_data, layout.size);
vkUnmapMemory(*device_, device_memory_);
dfn.vkUnmapMemory(*device_, device_memory_);
}
return status;
@@ -328,6 +340,8 @@ class VulkanImmediateTexture : public ImmediateTexture {
// the command buffer WILL be queued and executed by the device.
void TransitionLayout(VkCommandBuffer command_buffer,
VkImageLayout new_layout) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkImageMemoryBarrier image_barrier;
image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_barrier.pNext = nullptr;
@@ -342,16 +356,16 @@ class VulkanImmediateTexture : public ImmediateTexture {
image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_layout_ = new_layout;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &image_barrier);
dfn.vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr,
0, nullptr, 1, &image_barrier);
}
VkDescriptorSet descriptor_set() const { return descriptor_set_; }
VkImageLayout layout() const { return image_layout_; }
private:
ui::vulkan::VulkanDevice* device_ = nullptr;
VulkanDevice* device_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkSampler sampler_ = nullptr; // Not owned.
VkImage image_ = nullptr;
@@ -367,7 +381,8 @@ VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); }
VkResult VulkanImmediateDrawer::Initialize() {
auto device = context_->device();
const VulkanDevice* device = context_->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
// NEAREST + CLAMP
VkSamplerCreateInfo sampler_info;
@@ -389,8 +404,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
sampler_info.maxLod = 0.0f;
sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
sampler_info.unnormalizedCoordinates = VK_FALSE;
auto status = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_clamp);
auto status = dfn.vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_clamp);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -400,8 +415,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
status = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_repeat);
status = dfn.vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_repeat);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -413,8 +428,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
status =
vkCreateSampler(*device, &sampler_info, nullptr, &samplers_.linear_clamp);
status = dfn.vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.linear_clamp);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -424,8 +439,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
status = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.linear_repeat);
status = dfn.vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.linear_repeat);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
@@ -447,8 +462,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_binding.pImmutableSamplers = nullptr;
texture_set_layout_info.pBindings = &texture_binding;
status = vkCreateDescriptorSetLayout(*device, &texture_set_layout_info,
nullptr, &texture_set_layout_);
status = dfn.vkCreateDescriptorSetLayout(*device, &texture_set_layout_info,
nullptr, &texture_set_layout_);
CheckResult(status, "vkCreateDescriptorSetLayout");
if (status != VK_SUCCESS) {
return status;
@@ -468,8 +483,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
pool_sizes[0].descriptorCount = 128;
descriptor_pool_info.poolSizeCount = 1;
descriptor_pool_info.pPoolSizes = pool_sizes;
status = vkCreateDescriptorPool(*device, &descriptor_pool_info, nullptr,
&descriptor_pool_);
status = dfn.vkCreateDescriptorPool(*device, &descriptor_pool_info, nullptr,
&descriptor_pool_);
CheckResult(status, "vkCreateDescriptorPool");
if (status != VK_SUCCESS) {
return status;
@@ -495,8 +510,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
pipeline_layout_info.pushConstantRangeCount =
static_cast<uint32_t>(xe::countof(push_constant_ranges));
pipeline_layout_info.pPushConstantRanges = push_constant_ranges;
status = vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr,
&pipeline_layout_);
status = dfn.vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr,
&pipeline_layout_);
CheckResult(status, "vkCreatePipelineLayout");
if (status != VK_SUCCESS) {
return status;
@@ -510,8 +525,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
vertex_shader_info.codeSize = sizeof(immediate_vert);
vertex_shader_info.pCode = reinterpret_cast<const uint32_t*>(immediate_vert);
VkShaderModule vertex_shader;
status = vkCreateShaderModule(*device, &vertex_shader_info, nullptr,
&vertex_shader);
status = dfn.vkCreateShaderModule(*device, &vertex_shader_info, nullptr,
&vertex_shader);
CheckResult(status, "vkCreateShaderModule");
VkShaderModuleCreateInfo fragment_shader_info;
fragment_shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
@@ -521,8 +536,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
fragment_shader_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_frag);
VkShaderModule fragment_shader;
status = vkCreateShaderModule(*device, &fragment_shader_info, nullptr,
&fragment_shader);
status = dfn.vkCreateShaderModule(*device, &fragment_shader_info, nullptr,
&fragment_shader);
CheckResult(status, "vkCreateShaderModule");
// Pipeline used when rendering triangles.
@@ -667,8 +682,8 @@ VkResult VulkanImmediateDrawer::Initialize() {
pipeline_info.basePipelineHandle = nullptr;
pipeline_info.basePipelineIndex = -1;
if (status == VK_SUCCESS) {
status = vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info,
nullptr, &triangle_pipeline_);
status = dfn.vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info,
nullptr, &triangle_pipeline_);
CheckResult(status, "vkCreateGraphicsPipelines");
}
@@ -678,13 +693,13 @@ VkResult VulkanImmediateDrawer::Initialize() {
pipeline_info.basePipelineHandle = triangle_pipeline_;
pipeline_info.basePipelineIndex = -1;
if (status == VK_SUCCESS) {
status = vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info,
nullptr, &line_pipeline_);
status = dfn.vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info,
nullptr, &line_pipeline_);
CheckResult(status, "vkCreateGraphicsPipelines");
}
VK_SAFE_DESTROY(vkDestroyShaderModule, *device, vertex_shader, nullptr);
VK_SAFE_DESTROY(vkDestroyShaderModule, *device, fragment_shader, nullptr);
DestroyAndNullHandle(dfn.vkDestroyShaderModule, *device, vertex_shader);
DestroyAndNullHandle(dfn.vkDestroyShaderModule, *device, fragment_shader);
// Allocate the buffer we'll use for our vertex and index data.
circular_buffer_ = std::make_unique<LightweightCircularBuffer>(device);
@@ -693,22 +708,23 @@ VkResult VulkanImmediateDrawer::Initialize() {
}
void VulkanImmediateDrawer::Shutdown() {
auto device = context_->device();
const VulkanDevice* device = context_->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
circular_buffer_.reset();
VK_SAFE_DESTROY(vkDestroyPipeline, *device, line_pipeline_, nullptr);
VK_SAFE_DESTROY(vkDestroyPipeline, *device, triangle_pipeline_, nullptr);
VK_SAFE_DESTROY(vkDestroyPipelineLayout, *device, pipeline_layout_, nullptr);
DestroyAndNullHandle(dfn.vkDestroyPipeline, *device, line_pipeline_);
DestroyAndNullHandle(dfn.vkDestroyPipeline, *device, triangle_pipeline_);
DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, *device, pipeline_layout_);
VK_SAFE_DESTROY(vkDestroyDescriptorPool, *device, descriptor_pool_, nullptr);
VK_SAFE_DESTROY(vkDestroyDescriptorSetLayout, *device, texture_set_layout_,
nullptr);
DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, *device, descriptor_pool_);
DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, *device,
texture_set_layout_);
VK_SAFE_DESTROY(vkDestroySampler, *device, samplers_.nearest_clamp, nullptr);
VK_SAFE_DESTROY(vkDestroySampler, *device, samplers_.nearest_repeat, nullptr);
VK_SAFE_DESTROY(vkDestroySampler, *device, samplers_.linear_clamp, nullptr);
VK_SAFE_DESTROY(vkDestroySampler, *device, samplers_.linear_repeat, nullptr);
DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.nearest_clamp);
DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.nearest_repeat);
DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.linear_clamp);
DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.linear_repeat);
}
std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::CreateTexture(
@@ -751,7 +767,8 @@ std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::WrapTexture(
void VulkanImmediateDrawer::Begin(int render_target_width,
int render_target_height) {
auto device = context_->device();
const VulkanDevice* device = context_->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
auto swap_chain = context_->swap_chain();
assert_null(current_cmd_buffer_);
current_cmd_buffer_ = swap_chain->render_cmd_buffer();
@@ -766,7 +783,7 @@ void VulkanImmediateDrawer::Begin(int render_target_width,
viewport.height = static_cast<float>(render_target_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(current_cmd_buffer_, 0, 1, &viewport);
dfn.vkCmdSetViewport(current_cmd_buffer_, 0, 1, &viewport);
// Update projection matrix.
const float ortho_projection[4][4] = {
@@ -775,13 +792,14 @@ void VulkanImmediateDrawer::Begin(int render_target_width,
{0.0f, 0.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f, 1.0f},
};
vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 16,
ortho_projection);
dfn.vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 16,
ortho_projection);
}
void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
auto device = context_->device();
const VulkanDevice* device = context_->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
// Upload vertices.
VkDeviceSize vertices_offset = circular_buffer_->Emplace(
@@ -791,8 +809,8 @@ void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
return;
}
auto vertex_buffer = circular_buffer_->vertex_buffer();
vkCmdBindVertexBuffers(current_cmd_buffer_, 0, 1, &vertex_buffer,
&vertices_offset);
dfn.vkCmdBindVertexBuffers(current_cmd_buffer_, 0, 1, &vertex_buffer,
&vertices_offset);
// Upload indices.
if (batch.indices) {
@@ -802,22 +820,27 @@ void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
// TODO(benvanik): die?
return;
}
vkCmdBindIndexBuffer(current_cmd_buffer_, circular_buffer_->index_buffer(),
indices_offset, VK_INDEX_TYPE_UINT16);
dfn.vkCmdBindIndexBuffer(current_cmd_buffer_,
circular_buffer_->index_buffer(), indices_offset,
VK_INDEX_TYPE_UINT16);
}
batch_has_index_buffer_ = !!batch.indices;
}
void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
const VulkanDevice* device = context_->device();
const VulkanDevice::DeviceFunctions& dfn = device->dfn();
switch (draw.primitive_type) {
case ImmediatePrimitiveType::kLines:
vkCmdBindPipeline(current_cmd_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS,
line_pipeline_);
dfn.vkCmdBindPipeline(current_cmd_buffer_,
VK_PIPELINE_BIND_POINT_GRAPHICS, line_pipeline_);
break;
case ImmediatePrimitiveType::kTriangles:
vkCmdBindPipeline(current_cmd_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS,
triangle_pipeline_);
dfn.vkCmdBindPipeline(current_cmd_buffer_,
VK_PIPELINE_BIND_POINT_GRAPHICS,
triangle_pipeline_);
break;
}
@@ -833,18 +856,18 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
XELOGW("Failed to acquire texture descriptor set for immediate drawer!");
}
vkCmdBindDescriptorSets(current_cmd_buffer_,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_,
0, 1, &texture_set, 0, nullptr);
dfn.vkCmdBindDescriptorSets(
current_cmd_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_,
0, 1, &texture_set, 0, nullptr);
}
// Use push constants for our per-draw changes.
// Here, the restrict_texture_samples uniform (was used before September 26,
// 2020, now deleted).
int restrict_texture_samples = 0;
vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 16,
sizeof(int), &restrict_texture_samples);
dfn.vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 16,
sizeof(int), &restrict_texture_samples);
// Scissor, if enabled.
// Scissor can be disabled by making it the full screen.
@@ -861,14 +884,14 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
scissor.extent.width = current_render_target_width_;
scissor.extent.height = current_render_target_height_;
}
vkCmdSetScissor(current_cmd_buffer_, 0, 1, &scissor);
dfn.vkCmdSetScissor(current_cmd_buffer_, 0, 1, &scissor);
// Issue draw.
if (batch_has_index_buffer_) {
vkCmdDrawIndexed(current_cmd_buffer_, draw.count, 1, draw.index_offset,
draw.base_vertex, 0);
dfn.vkCmdDrawIndexed(current_cmd_buffer_, draw.count, 1, draw.index_offset,
draw.base_vertex, 0);
} else {
vkCmdDraw(current_cmd_buffer_, draw.count, 1, draw.base_vertex, 0);
dfn.vkCmdDraw(current_cmd_buffer_, draw.count, 1, draw.base_vertex, 0);
}
}

View File

@@ -10,15 +10,16 @@
#include "xenia/ui/vulkan/vulkan_instance.h"
#include <cinttypes>
#include <cstring>
#include <mutex>
#include <string>
#include "third_party/renderdoc/renderdoc_app.h"
#include "third_party/volk/volk.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/platform.h"
#include "xenia/base/profiling.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
@@ -26,6 +27,12 @@
#include "xenia/ui/window.h"
#if XE_PLATFORM_LINUX
#include <dlfcn.h>
#elif XE_PLATFORM_WIN32
#include "xenia/base/platform_win.h"
#endif
#if XE_PLATFORM_GNU_LINUX
#include "xenia/ui/window_gtk.h"
#endif
@@ -64,21 +71,6 @@ VulkanInstance::VulkanInstance() {
DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
DeclareRequiredExtension(VK_KHR_SURFACE_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
#if XE_PLATFORM_WIN32
DeclareRequiredExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
#elif XE_PLATFORM_LINUX
#ifdef GDK_WINDOWING_X11
DeclareRequiredExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
#else
#error No Vulkan surface extension for the GDK backend defined yet.
#endif
#else
#error No Vulkan surface extension for the platform defined yet.
#endif
}
VulkanInstance::~VulkanInstance() { DestroyInstance(); }
@@ -86,8 +78,61 @@ VulkanInstance::~VulkanInstance() { DestroyInstance(); }
bool VulkanInstance::Initialize() {
auto version = Version::Parse(VK_API_VERSION);
XELOGVK("Initializing Vulkan {}...", version.pretty_string);
if (volkInitialize() != VK_SUCCESS) {
XELOGE("volkInitialize() failed!");
// Load the library.
bool library_functions_loaded = true;
#if XE_PLATFORM_LINUX
#if XE_PLATFORM_ANDROID
const char* libvulkan_name = "libvulkan.so";
#else
const char* libvulkan_name = "libvulkan.so.1";
#endif
// http://developer.download.nvidia.com/mobile/shield/assets/Vulkan/UsingtheVulkanAPI.pdf
library_ = dlopen(libvulkan_name, RTLD_NOW | RTLD_LOCAL);
if (!library_) {
XELOGE("Failed to load {}", libvulkan_name);
return false;
}
#define XE_VULKAN_LOAD_MODULE_LFN(name) \
library_functions_loaded &= \
(lfn_.name = PFN_##name(dlsym(library_, #name))) != nullptr;
#elif XE_PLATFORM_WIN32
library_ = LoadLibraryA("vulkan-1.dll");
if (!library_) {
XELOGE("Failed to load vulkan-1.dll");
return false;
}
#define XE_VULKAN_LOAD_MODULE_LFN(name) \
library_functions_loaded &= \
(lfn_.name = PFN_##name(GetProcAddress(library_, #name))) != nullptr;
#else
#error No Vulkan library loading provided for the target platform.
#endif
XE_VULKAN_LOAD_MODULE_LFN(vkGetInstanceProcAddr);
XE_VULKAN_LOAD_MODULE_LFN(vkDestroyInstance);
#undef XE_VULKAN_LOAD_MODULE_LFN
if (!library_functions_loaded) {
XELOGE("Failed to get Vulkan library function pointers");
return false;
}
library_functions_loaded &=
(lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr;
library_functions_loaded &=
(lfn_.vkEnumerateInstanceExtensionProperties =
PFN_vkEnumerateInstanceExtensionProperties(
lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE,
"vkEnumerateInstanceExtensionProperties"))) != nullptr;
library_functions_loaded &=
(lfn_.vkEnumerateInstanceLayerProperties =
PFN_vkEnumerateInstanceLayerProperties(lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"))) !=
nullptr;
if (!library_functions_loaded) {
XELOGE(
"Failed to get Vulkan library function pointers via "
"vkGetInstanceProcAddr");
return false;
}
@@ -164,11 +209,11 @@ bool VulkanInstance::QueryGlobals() {
std::vector<VkLayerProperties> global_layer_properties;
VkResult err;
do {
err = vkEnumerateInstanceLayerProperties(&count, nullptr);
err = lfn_.vkEnumerateInstanceLayerProperties(&count, nullptr);
CheckResult(err, "vkEnumerateInstanceLayerProperties");
global_layer_properties.resize(count);
err = vkEnumerateInstanceLayerProperties(&count,
global_layer_properties.data());
err = lfn_.vkEnumerateInstanceLayerProperties(
&count, global_layer_properties.data());
} while (err == VK_INCOMPLETE);
CheckResult(err, "vkEnumerateInstanceLayerProperties");
global_layers_.resize(count);
@@ -178,11 +223,11 @@ bool VulkanInstance::QueryGlobals() {
// Get all extensions available for the layer.
do {
err = vkEnumerateInstanceExtensionProperties(
err = lfn_.vkEnumerateInstanceExtensionProperties(
global_layer.properties.layerName, &count, nullptr);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
global_layer.extensions.resize(count);
err = vkEnumerateInstanceExtensionProperties(
err = lfn_.vkEnumerateInstanceExtensionProperties(
global_layer.properties.layerName, &count,
global_layer.extensions.data());
} while (err == VK_INCOMPLETE);
@@ -205,11 +250,11 @@ bool VulkanInstance::QueryGlobals() {
// Scan global extensions.
do {
err = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
err = lfn_.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
global_extensions_.resize(count);
err = vkEnumerateInstanceExtensionProperties(nullptr, &count,
global_extensions_.data());
err = lfn_.vkEnumerateInstanceExtensionProperties(
nullptr, &count, global_extensions_.data());
} while (err == VK_INCOMPLETE);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
XELOGVK("Found {} global extensions:", global_extensions_.size());
@@ -218,6 +263,136 @@ bool VulkanInstance::QueryGlobals() {
return true;
}
bool VulkanInstance::CreateInstance() {
XELOGVK("Verifying layers and extensions...");
// Gather list of enabled layer names.
auto layers_result = CheckRequirements(required_layers_, global_layers_);
auto& enabled_layers = layers_result.second;
// Gather list of enabled extension names.
auto extensions_result =
CheckRequirements(required_extensions_, global_extensions_);
auto& enabled_extensions = extensions_result.second;
// We wait until both extensions and layers are checked before failing out so
// that the user gets a complete list of what they have/don't.
if (!extensions_result.first || !layers_result.first) {
XELOGE("Layer and extension verification failed; aborting initialization");
return false;
}
XELOGVK("Initializing application instance...");
// TODO(benvanik): use GetEntryInfo?
VkApplicationInfo application_info;
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = nullptr;
application_info.pApplicationName = "xenia";
application_info.applicationVersion = 1;
application_info.pEngineName = "xenia";
application_info.engineVersion = 1;
application_info.apiVersion = VK_API_VERSION;
VkInstanceCreateInfo instance_info;
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_info.pNext = nullptr;
instance_info.flags = 0;
instance_info.pApplicationInfo = &application_info;
instance_info.enabledLayerCount =
static_cast<uint32_t>(enabled_layers.size());
instance_info.ppEnabledLayerNames = enabled_layers.data();
instance_info.enabledExtensionCount =
static_cast<uint32_t>(enabled_extensions.size());
instance_info.ppEnabledExtensionNames = enabled_extensions.data();
auto err = lfn_.vkCreateInstance(&instance_info, nullptr, &handle);
if (err != VK_SUCCESS) {
XELOGE("vkCreateInstance returned {}", to_string(err));
}
switch (err) {
case VK_SUCCESS:
// Ok!
break;
case VK_ERROR_INITIALIZATION_FAILED:
XELOGE("Instance initialization failed; generic");
return false;
case VK_ERROR_INCOMPATIBLE_DRIVER:
XELOGE(
"Instance initialization failed; cannot find a compatible Vulkan "
"installable client driver (ICD)");
return false;
case VK_ERROR_EXTENSION_NOT_PRESENT:
XELOGE("Instance initialization failed; requested extension not present");
return false;
case VK_ERROR_LAYER_NOT_PRESENT:
XELOGE("Instance initialization failed; requested layer not present");
return false;
default:
XELOGE("Instance initialization failed; unknown: {}", to_string(err));
return false;
}
// Check if extensions are enabled.
dbg_report_ena_ = false;
for (const char* enabled_extension : enabled_extensions) {
if (!dbg_report_ena_ &&
!std::strcmp(enabled_extension, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
dbg_report_ena_ = true;
}
}
// Get instance functions.
std::memset(&ifn_, 0, sizeof(ifn_));
bool instance_functions_loaded = true;
#define XE_UI_VULKAN_FUNCTION(name) \
instance_functions_loaded &= \
(ifn_.name = PFN_##name(lfn_.vkGetInstanceProcAddr(handle, #name))) != \
nullptr;
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
#if XE_PLATFORM_ANDROID
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
#elif XE_PLATFORM_GNU_LINUX
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
#elif XE_PLATFORM_WIN32
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
#endif
if (dbg_report_ena_) {
#include "xenia/ui/vulkan/functions/instance_ext_debug_report.inc"
}
#undef XE_VULKAN_LOAD_IFN
if (!instance_functions_loaded) {
XELOGE("Failed to get Vulkan instance function pointers");
return false;
}
// Enable debug validation, if needed.
EnableDebugValidation();
return true;
}
void VulkanInstance::DestroyInstance() {
if (handle) {
DisableDebugValidation();
lfn_.vkDestroyInstance(handle, nullptr);
handle = nullptr;
}
#if XE_PLATFORM_LINUX
if (library_) {
dlclose(library_);
library_ = nullptr;
}
#elif XE_PLATFORM_WIN32
if (library_) {
FreeLibrary(library_);
library_ = nullptr;
}
#endif
}
VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location,
@@ -255,116 +430,14 @@ VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags,
return false;
}
bool VulkanInstance::CreateInstance() {
XELOGVK("Verifying layers and extensions...");
// Gather list of enabled layer names.
auto layers_result = CheckRequirements(required_layers_, global_layers_);
auto& enabled_layers = layers_result.second;
// Gather list of enabled extension names.
auto extensions_result =
CheckRequirements(required_extensions_, global_extensions_);
auto& enabled_extensions = extensions_result.second;
// We wait until both extensions and layers are checked before failing out so
// that the user gets a complete list of what they have/don't.
if (!extensions_result.first || !layers_result.first) {
XELOGE("Layer and extension verification failed; aborting initialization");
return false;
}
XELOGVK("Initializing application instance...");
VkDebugReportCallbackCreateInfoEXT debug_info;
debug_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
debug_info.pNext = nullptr;
// TODO(benvanik): flags to set these.
debug_info.flags =
VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT;
debug_info.pfnCallback = &DebugMessageCallback;
debug_info.pUserData = this;
// TODO(benvanik): use GetEntryInfo?
VkApplicationInfo application_info;
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = &debug_info;
application_info.pApplicationName = "xenia";
application_info.applicationVersion = 1;
application_info.pEngineName = "xenia";
application_info.engineVersion = 1;
application_info.apiVersion = VK_API_VERSION;
VkInstanceCreateInfo instance_info;
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_info.pNext = nullptr;
instance_info.flags = 0;
instance_info.pApplicationInfo = &application_info;
instance_info.enabledLayerCount =
static_cast<uint32_t>(enabled_layers.size());
instance_info.ppEnabledLayerNames = enabled_layers.data();
instance_info.enabledExtensionCount =
static_cast<uint32_t>(enabled_extensions.size());
instance_info.ppEnabledExtensionNames = enabled_extensions.data();
auto err = vkCreateInstance(&instance_info, nullptr, &handle);
if (err != VK_SUCCESS) {
XELOGE("vkCreateInstance returned {}", to_string(err));
}
switch (err) {
case VK_SUCCESS:
// Ok!
break;
case VK_ERROR_INITIALIZATION_FAILED:
XELOGE("Instance initialization failed; generic");
return false;
case VK_ERROR_INCOMPATIBLE_DRIVER:
XELOGE(
"Instance initialization failed; cannot find a compatible Vulkan "
"installable client driver (ICD)");
return false;
case VK_ERROR_EXTENSION_NOT_PRESENT:
XELOGE("Instance initialization failed; requested extension not present");
return false;
case VK_ERROR_LAYER_NOT_PRESENT:
XELOGE("Instance initialization failed; requested layer not present");
return false;
default:
XELOGE("Instance initialization failed; unknown: {}", to_string(err));
return false;
}
// Load Vulkan entry points and extensions.
volkLoadInstance(handle);
// Enable debug validation, if needed.
EnableDebugValidation();
return true;
}
void VulkanInstance::DestroyInstance() {
if (!handle) {
return;
}
DisableDebugValidation();
vkDestroyInstance(handle, nullptr);
handle = nullptr;
}
void VulkanInstance::EnableDebugValidation() {
if (dbg_report_callback_) {
DisableDebugValidation();
}
auto vk_create_debug_report_callback_ext =
reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
vkGetInstanceProcAddr(handle, "vkCreateDebugReportCallbackEXT"));
if (!vk_create_debug_report_callback_ext) {
if (!dbg_report_ena_) {
XELOGVK("Debug validation layer not installed; ignoring");
return;
}
if (dbg_report_callback_) {
DisableDebugValidation();
}
VkDebugReportCallbackCreateInfoEXT create_info;
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
create_info.pNext = nullptr;
@@ -375,7 +448,7 @@ void VulkanInstance::EnableDebugValidation() {
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT;
create_info.pfnCallback = &DebugMessageCallback;
create_info.pUserData = this;
auto status = vk_create_debug_report_callback_ext(
auto status = ifn_.vkCreateDebugReportCallbackEXT(
handle, &create_info, nullptr, &dbg_report_callback_);
if (status == VK_SUCCESS) {
XELOGVK("Debug validation layer enabled");
@@ -386,16 +459,10 @@ void VulkanInstance::EnableDebugValidation() {
}
void VulkanInstance::DisableDebugValidation() {
if (!dbg_report_callback_) {
if (!dbg_report_ena_ || !dbg_report_callback_) {
return;
}
auto vk_destroy_debug_report_callback_ext =
reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
vkGetInstanceProcAddr(handle, "vkDestroyDebugReportCallbackEXT"));
if (!vk_destroy_debug_report_callback_ext) {
return;
}
vk_destroy_debug_report_callback_ext(handle, dbg_report_callback_, nullptr);
ifn_.vkDestroyDebugReportCallbackEXT(handle, dbg_report_callback_, nullptr);
dbg_report_callback_ = nullptr;
}
@@ -403,11 +470,11 @@ bool VulkanInstance::QueryDevices() {
// Get handles to all devices.
uint32_t count = 0;
std::vector<VkPhysicalDevice> device_handles;
auto err = vkEnumeratePhysicalDevices(handle, &count, nullptr);
auto err = ifn_.vkEnumeratePhysicalDevices(handle, &count, nullptr);
CheckResult(err, "vkEnumeratePhysicalDevices");
device_handles.resize(count);
err = vkEnumeratePhysicalDevices(handle, &count, device_handles.data());
err = ifn_.vkEnumeratePhysicalDevices(handle, &count, device_handles.data());
CheckResult(err, "vkEnumeratePhysicalDevices");
// Query device info.
@@ -417,33 +484,34 @@ bool VulkanInstance::QueryDevices() {
device_info.handle = device_handle;
// Query general attributes.
vkGetPhysicalDeviceProperties(device_handle, &device_info.properties);
vkGetPhysicalDeviceFeatures(device_handle, &device_info.features);
vkGetPhysicalDeviceMemoryProperties(device_handle,
&device_info.memory_properties);
ifn_.vkGetPhysicalDeviceProperties(device_handle, &device_info.properties);
ifn_.vkGetPhysicalDeviceFeatures(device_handle, &device_info.features);
ifn_.vkGetPhysicalDeviceMemoryProperties(device_handle,
&device_info.memory_properties);
// Gather queue family properties.
vkGetPhysicalDeviceQueueFamilyProperties(device_handle, &count, nullptr);
ifn_.vkGetPhysicalDeviceQueueFamilyProperties(device_handle, &count,
nullptr);
device_info.queue_family_properties.resize(count);
vkGetPhysicalDeviceQueueFamilyProperties(
ifn_.vkGetPhysicalDeviceQueueFamilyProperties(
device_handle, &count, device_info.queue_family_properties.data());
// Gather layers.
std::vector<VkLayerProperties> layer_properties;
err = vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr);
err = ifn_.vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr);
CheckResult(err, "vkEnumerateDeviceLayerProperties");
layer_properties.resize(count);
err = vkEnumerateDeviceLayerProperties(device_handle, &count,
layer_properties.data());
err = ifn_.vkEnumerateDeviceLayerProperties(device_handle, &count,
layer_properties.data());
CheckResult(err, "vkEnumerateDeviceLayerProperties");
for (size_t j = 0; j < layer_properties.size(); ++j) {
LayerInfo layer_info;
layer_info.properties = layer_properties[j];
err = vkEnumerateDeviceExtensionProperties(
err = ifn_.vkEnumerateDeviceExtensionProperties(
device_handle, layer_info.properties.layerName, &count, nullptr);
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
layer_info.extensions.resize(count);
err = vkEnumerateDeviceExtensionProperties(
err = ifn_.vkEnumerateDeviceExtensionProperties(
device_handle, layer_info.properties.layerName, &count,
layer_info.extensions.data());
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
@@ -451,12 +519,12 @@ bool VulkanInstance::QueryDevices() {
}
// Gather extensions.
err = vkEnumerateDeviceExtensionProperties(device_handle, nullptr, &count,
nullptr);
err = ifn_.vkEnumerateDeviceExtensionProperties(device_handle, nullptr,
&count, nullptr);
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
device_info.extensions.resize(count);
err = vkEnumerateDeviceExtensionProperties(device_handle, nullptr, &count,
device_info.extensions.data());
err = ifn_.vkEnumerateDeviceExtensionProperties(
device_handle, nullptr, &count, device_info.extensions.data());
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
available_devices_.push_back(std::move(device_info));

View File

@@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include "xenia/base/platform.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
@@ -28,10 +29,38 @@ class VulkanInstance {
VulkanInstance();
~VulkanInstance();
struct LibraryFunctions {
// From the module.
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkDestroyInstance vkDestroyInstance;
// From vkGetInstanceProcAddr.
PFN_vkCreateInstance vkCreateInstance;
PFN_vkEnumerateInstanceExtensionProperties
vkEnumerateInstanceExtensionProperties;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
};
const LibraryFunctions& lfn() const { return lfn_; }
VkInstance handle = nullptr;
operator VkInstance() const { return handle; }
struct InstanceFunctions {
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name;
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
#include "xenia/ui/vulkan/functions/instance_ext_debug_report.inc"
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
#if XE_PLATFORM_ANDROID
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
#elif XE_PLATFORM_GNU_LINUX
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
#elif XE_PLATFORM_WIN32
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
#endif
#undef XE_UI_VULKAN_FUNCTION
};
const InstanceFunctions& ifn() const { return ifn_; }
// Declares a layer to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredLayer(std::string name, uint32_t min_version,
@@ -85,13 +114,24 @@ class VulkanInstance {
const char* indent);
void DumpDeviceInfo(const DeviceInfo& device_info);
#if XE_PLATFORM_LINUX
void* library_ = nullptr;
#elif XE_PLATFORM_WIN32
HMODULE library_ = nullptr;
#endif
LibraryFunctions lfn_ = {};
std::vector<Requirement> required_layers_;
std::vector<Requirement> required_extensions_;
InstanceFunctions ifn_ = {};
std::vector<LayerInfo> global_layers_;
std::vector<VkExtensionProperties> global_extensions_;
std::vector<DeviceInfo> available_devices_;
bool dbg_report_ena_ = false;
VkDebugReportCallbackEXT dbg_report_callback_ = nullptr;
void* renderdoc_api_ = nullptr;

View File

@@ -10,31 +10,35 @@
#ifndef XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_
#define XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_
#include "third_party/volk/volk.h"
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#include "third_party/vulkan/vk_mem_alloc.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_instance.h"
namespace xe {
namespace ui {
namespace vulkan {
inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs) {
vma_funcs->vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs,
const VulkanDevice& device) {
const VulkanInstance::InstanceFunctions& ifn = device.instance()->ifn();
const VulkanDevice::DeviceFunctions& dfn = device.dfn();
vma_funcs->vkGetPhysicalDeviceProperties = ifn.vkGetPhysicalDeviceProperties;
vma_funcs->vkGetPhysicalDeviceMemoryProperties =
vkGetPhysicalDeviceMemoryProperties;
vma_funcs->vkAllocateMemory = vkAllocateMemory;
vma_funcs->vkFreeMemory = vkFreeMemory;
vma_funcs->vkMapMemory = vkMapMemory;
vma_funcs->vkUnmapMemory = vkUnmapMemory;
vma_funcs->vkBindBufferMemory = vkBindBufferMemory;
vma_funcs->vkBindImageMemory = vkBindImageMemory;
vma_funcs->vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
vma_funcs->vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
vma_funcs->vkCreateBuffer = vkCreateBuffer;
vma_funcs->vkDestroyBuffer = vkDestroyBuffer;
vma_funcs->vkCreateImage = vkCreateImage;
vma_funcs->vkDestroyImage = vkDestroyImage;
ifn.vkGetPhysicalDeviceMemoryProperties;
vma_funcs->vkAllocateMemory = dfn.vkAllocateMemory;
vma_funcs->vkFreeMemory = dfn.vkFreeMemory;
vma_funcs->vkMapMemory = dfn.vkMapMemory;
vma_funcs->vkUnmapMemory = dfn.vkUnmapMemory;
vma_funcs->vkBindBufferMemory = dfn.vkBindBufferMemory;
vma_funcs->vkBindImageMemory = dfn.vkBindImageMemory;
vma_funcs->vkGetBufferMemoryRequirements = dfn.vkGetBufferMemoryRequirements;
vma_funcs->vkGetImageMemoryRequirements = dfn.vkGetImageMemoryRequirements;
vma_funcs->vkCreateBuffer = dfn.vkCreateBuffer;
vma_funcs->vkDestroyBuffer = dfn.vkDestroyBuffer;
vma_funcs->vkCreateImage = dfn.vkCreateImage;
vma_funcs->vkDestroyImage = dfn.vkDestroyImage;
}
} // namespace vulkan

View File

@@ -52,14 +52,16 @@ bool VulkanProvider::Initialize() {
instance_ = std::make_unique<VulkanInstance>();
// Always enable the swapchain.
instance_->DeclareRequiredExtension(VK_KHR_SURFACE_EXTENSION_NAME,
#if XE_PLATFORM_GNU_LINUX || XE_PLATFORM_WIN32
instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0),
false);
#if XE_PLATFORM_GNU_LINUX
instance_->DeclareRequiredExtension("VK_KHR_xcb_surface",
Version::Make(0, 0, 0), false);
#if XE_PLATFORM_WIN32
instance_->DeclareRequiredExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
Version::Make(0, 0, 0), false);
#elif XE_PLATFORM_LINUX
instance_->DeclareRequiredExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME,
#elif XE_PLATFORM_WIN32
instance_->DeclareRequiredExtension("VK_KHR_win32_surface",
Version::Make(0, 0, 0), false);
#endif
#endif
// Attempt initialization and device query.

View File

@@ -35,6 +35,8 @@ VulkanSwapChain::~VulkanSwapChain() { Shutdown(); }
VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
surface_ = surface;
const VulkanInstance::InstanceFunctions& ifn = instance_->ifn();
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
// Find a queue family that supports presentation.
@@ -49,8 +51,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
continue;
}
status = vkGetPhysicalDeviceSurfaceSupportKHR(*device_, i, surface,
&surface_supported);
status = ifn.vkGetPhysicalDeviceSurfaceSupportKHR(*device_, i, surface,
&surface_supported);
if (status == VK_SUCCESS && surface_supported == VK_TRUE) {
queue_family_index = i;
break;
@@ -80,13 +82,13 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
// Query supported target formats.
uint32_t count = 0;
status =
vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, nullptr);
status = ifn.vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count,
nullptr);
CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR");
std::vector<VkSurfaceFormatKHR> surface_formats;
surface_formats.resize(count);
status = vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count,
surface_formats.data());
status = ifn.vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count,
surface_formats.data());
CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR");
if (status != VK_SUCCESS) {
return status;
@@ -107,8 +109,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
// Query surface min/max/caps.
VkSurfaceCapabilitiesKHR surface_caps;
status = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_,
&surface_caps);
status = ifn.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_,
&surface_caps);
CheckResult(status, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
if (status != VK_SUCCESS) {
return status;
@@ -116,16 +118,16 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
// Query surface properties so we can configure ourselves within bounds.
std::vector<VkPresentModeKHR> present_modes;
status = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
nullptr);
status = ifn.vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_,
&count, nullptr);
CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (status != VK_SUCCESS) {
return status;
}
present_modes.resize(count);
status = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
present_modes.data());
status = ifn.vkGetPhysicalDeviceSurfacePresentModesKHR(
*device_, surface_, &count, present_modes.data());
CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (status != VK_SUCCESS) {
return status;
@@ -210,7 +212,7 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
XELOGVK(" imageSharingMode = {}", to_string(create_info.imageSharingMode));
XELOGVK(" queueFamilyCount = {}", create_info.queueFamilyIndexCount);
status = vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle);
status = dfn.vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle);
if (status != VK_SUCCESS) {
XELOGE("Failed to create swapchain: {}", to_string(status));
return status;
@@ -223,7 +225,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
cmd_pool_info.pNext = nullptr;
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
cmd_pool_info.queueFamilyIndex = presentation_queue_family_;
status = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
status =
dfn.vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
CheckResult(status, "vkCreateCommandPool");
if (status != VK_SUCCESS) {
return status;
@@ -236,7 +239,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
cmd_buffer_info.commandPool = cmd_pool_;
cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_buffer_info.commandBufferCount = 2;
status = vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &cmd_buffer_);
status =
dfn.vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &cmd_buffer_);
CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -247,7 +251,7 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
cmd_buffer_info.commandBufferCount = 2;
status =
vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers);
dfn.vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers);
CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -296,8 +300,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
render_pass_info.pSubpasses = &render_subpass;
render_pass_info.dependencyCount = 0;
render_pass_info.pDependencies = nullptr;
status =
vkCreateRenderPass(*device_, &render_pass_info, nullptr, &render_pass_);
status = dfn.vkCreateRenderPass(*device_, &render_pass_info, nullptr,
&render_pass_);
CheckResult(status, "vkCreateRenderPass");
if (status != VK_SUCCESS) {
return status;
@@ -308,16 +312,16 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_info.pNext = nullptr;
semaphore_info.flags = 0;
status = vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_available_semaphore_);
status = dfn.vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_available_semaphore_);
CheckResult(status, "vkCreateSemaphore");
if (status != VK_SUCCESS) {
return status;
}
// Create another semaphore used to synchronize writes to the swap image.
status = vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_usage_semaphore_);
status = dfn.vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_usage_semaphore_);
CheckResult(status, "vkCreateSemaphore");
if (status != VK_SUCCESS) {
return status;
@@ -327,16 +331,16 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
// Note that this may differ from our requested amount.
uint32_t actual_image_count = 0;
std::vector<VkImage> images;
status =
vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, nullptr);
status = dfn.vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
nullptr);
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
images.resize(actual_image_count);
status = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
images.data());
status = dfn.vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
images.data());
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
@@ -360,8 +364,8 @@ VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
nullptr,
VK_FENCE_CREATE_SIGNALED_BIT,
};
status = vkCreateFence(*device_, &fence_create_info, nullptr,
&synchronization_fence_);
status = dfn.vkCreateFence(*device_, &fence_create_info, nullptr,
&synchronization_fence_);
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
@@ -376,6 +380,7 @@ VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
DestroyBuffer(buffer);
buffer->image = target_image;
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
// Create an image view for the presentation image.
@@ -396,8 +401,8 @@ VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
image_view_info.subresourceRange.levelCount = 1;
image_view_info.subresourceRange.baseArrayLayer = 0;
image_view_info.subresourceRange.layerCount = 1;
status = vkCreateImageView(*device_, &image_view_info, nullptr,
&buffer->image_view);
status = dfn.vkCreateImageView(*device_, &image_view_info, nullptr,
&buffer->image_view);
CheckResult(status, "vkCreateImageView");
if (status != VK_SUCCESS) {
return status;
@@ -416,8 +421,8 @@ VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
framebuffer_info.width = surface_width_;
framebuffer_info.height = surface_height_;
framebuffer_info.layers = 1;
status = vkCreateFramebuffer(*device_, &framebuffer_info, nullptr,
&buffer->framebuffer);
status = dfn.vkCreateFramebuffer(*device_, &framebuffer_info, nullptr,
&buffer->framebuffer);
CheckResult(status, "vkCreateFramebuffer");
if (status != VK_SUCCESS) {
return status;
@@ -427,12 +432,13 @@ VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
}
void VulkanSwapChain::DestroyBuffer(Buffer* buffer) {
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
if (buffer->framebuffer) {
vkDestroyFramebuffer(*device_, buffer->framebuffer, nullptr);
dfn.vkDestroyFramebuffer(*device_, buffer->framebuffer, nullptr);
buffer->framebuffer = nullptr;
}
if (buffer->image_view) {
vkDestroyImageView(*device_, buffer->image_view, nullptr);
dfn.vkDestroyImageView(*device_, buffer->image_view, nullptr);
buffer->image_view = nullptr;
}
// Image is taken care of by the presentation engine.
@@ -458,19 +464,21 @@ void VulkanSwapChain::Shutdown() {
}
buffers_.clear();
VK_SAFE_DESTROY(vkDestroySemaphore, *device_, image_available_semaphore_,
nullptr);
VK_SAFE_DESTROY(vkDestroyRenderPass, *device_, render_pass_, nullptr);
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
DestroyAndNullHandle(dfn.vkDestroySemaphore, *device_,
image_available_semaphore_);
DestroyAndNullHandle(dfn.vkDestroyRenderPass, *device_, render_pass_);
if (copy_cmd_buffer_) {
vkFreeCommandBuffers(*device_, cmd_pool_, 1, &copy_cmd_buffer_);
dfn.vkFreeCommandBuffers(*device_, cmd_pool_, 1, &copy_cmd_buffer_);
copy_cmd_buffer_ = nullptr;
}
if (render_cmd_buffer_) {
vkFreeCommandBuffers(*device_, cmd_pool_, 1, &render_cmd_buffer_);
dfn.vkFreeCommandBuffers(*device_, cmd_pool_, 1, &render_cmd_buffer_);
render_cmd_buffer_ = nullptr;
}
VK_SAFE_DESTROY(vkDestroyCommandPool, *device_, cmd_pool_, nullptr);
DestroyAndNullHandle(dfn.vkDestroyCommandPool, *device_, cmd_pool_);
if (presentation_queue_) {
if (!presentation_queue_mutex_) {
@@ -482,33 +490,36 @@ void VulkanSwapChain::Shutdown() {
presentation_queue_family_ = -1;
}
VK_SAFE_DESTROY(vkDestroyFence, *device_, synchronization_fence_, nullptr);
DestroyAndNullHandle(dfn.vkDestroyFence, *device_, synchronization_fence_);
// images_ doesn't need to be cleaned up as the swapchain does it implicitly.
VK_SAFE_DESTROY(vkDestroySwapchainKHR, *device_, handle, nullptr);
VK_SAFE_DESTROY(vkDestroySurfaceKHR, *instance_, surface_, nullptr);
DestroyAndNullHandle(dfn.vkDestroySwapchainKHR, *device_, handle);
const VulkanInstance::InstanceFunctions& ifn = instance_->ifn();
DestroyAndNullHandle(ifn.vkDestroySurfaceKHR, *instance_, surface_);
}
VkResult VulkanSwapChain::Begin() {
wait_semaphores_.clear();
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
// Wait for the last swap to finish.
status = vkWaitForFences(*device_, 1, &synchronization_fence_, VK_TRUE, -1);
status =
dfn.vkWaitForFences(*device_, 1, &synchronization_fence_, VK_TRUE, -1);
if (status != VK_SUCCESS) {
return status;
}
status = vkResetFences(*device_, 1, &synchronization_fence_);
status = dfn.vkResetFences(*device_, 1, &synchronization_fence_);
if (status != VK_SUCCESS) {
return status;
}
// Get the index of the next available swapchain image.
status =
vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_,
nullptr, &current_buffer_index_);
dfn.vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_,
nullptr, &current_buffer_index_);
if (status != VK_SUCCESS) {
return status;
}
@@ -531,7 +542,8 @@ VkResult VulkanSwapChain::Begin() {
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueueSubmit(presentation_queue_, 1, &wait_submit_info, nullptr);
status =
dfn.vkQueueSubmit(presentation_queue_, 1, &wait_submit_info, nullptr);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}
@@ -540,8 +552,8 @@ VkResult VulkanSwapChain::Begin() {
}
// Reset all command buffers.
vkResetCommandBuffer(render_cmd_buffer_, 0);
vkResetCommandBuffer(copy_cmd_buffer_, 0);
dfn.vkResetCommandBuffer(render_cmd_buffer_, 0);
dfn.vkResetCommandBuffer(copy_cmd_buffer_, 0);
auto& current_buffer = buffers_[current_buffer_index_];
// Build the command buffer that will execute all queued rendering buffers.
@@ -561,7 +573,7 @@ VkResult VulkanSwapChain::Begin() {
begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
begin_info.pInheritanceInfo = &inherit_info;
status = vkBeginCommandBuffer(render_cmd_buffer_, &begin_info);
status = dfn.vkBeginCommandBuffer(render_cmd_buffer_, &begin_info);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -569,7 +581,7 @@ VkResult VulkanSwapChain::Begin() {
// Start recording the copy command buffer as well.
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
status = vkBeginCommandBuffer(copy_cmd_buffer_, &begin_info);
status = dfn.vkBeginCommandBuffer(copy_cmd_buffer_, &begin_info);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -588,31 +600,32 @@ VkResult VulkanSwapChain::Begin() {
clear_color.float32[1] = 1.0f;
clear_color.float32[2] = 0.0f;
}
vkCmdClearColorImage(copy_cmd_buffer_, current_buffer.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1,
&clear_range);
dfn.vkCmdClearColorImage(copy_cmd_buffer_, current_buffer.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color,
1, &clear_range);
return VK_SUCCESS;
}
VkResult VulkanSwapChain::End() {
auto& current_buffer = buffers_[current_buffer_index_];
const VulkanDevice::DeviceFunctions& dfn = device_->dfn();
VkResult status;
status = vkEndCommandBuffer(render_cmd_buffer_);
status = dfn.vkEndCommandBuffer(render_cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
status = vkEndCommandBuffer(copy_cmd_buffer_);
status = dfn.vkEndCommandBuffer(copy_cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// Build primary command buffer.
status = vkResetCommandBuffer(cmd_buffer_, 0);
status = dfn.vkResetCommandBuffer(cmd_buffer_, 0);
CheckResult(status, "vkResetCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -623,7 +636,7 @@ VkResult VulkanSwapChain::End() {
begin_info.pNext = nullptr;
begin_info.flags = 0;
begin_info.pInheritanceInfo = nullptr;
status = vkBeginCommandBuffer(cmd_buffer_, &begin_info);
status = dfn.vkBeginCommandBuffer(cmd_buffer_, &begin_info);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -642,14 +655,14 @@ VkResult VulkanSwapChain::End() {
pre_image_copy_barrier.image = current_buffer.image;
pre_image_copy_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
1};
vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
nullptr, 1, &pre_image_copy_barrier);
dfn.vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
nullptr, 1, &pre_image_copy_barrier);
current_buffer.image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// Execute copy commands
vkCmdExecuteCommands(cmd_buffer_, 1, &copy_cmd_buffer_);
dfn.vkCmdExecuteCommands(cmd_buffer_, 1, &copy_cmd_buffer_);
// Transition the image to a color attachment target for drawing.
VkImageMemoryBarrier pre_image_memory_barrier;
@@ -669,9 +682,9 @@ VkResult VulkanSwapChain::End() {
pre_image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
pre_image_memory_barrier.oldLayout = current_buffer.image_layout;
pre_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0,
nullptr, 0, nullptr, 1, &pre_image_memory_barrier);
dfn.vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0,
nullptr, 0, nullptr, 1, &pre_image_memory_barrier);
current_buffer.image_layout = pre_image_memory_barrier.newLayout;
@@ -687,14 +700,14 @@ VkResult VulkanSwapChain::End() {
render_pass_begin_info.renderArea.extent.height = surface_height_;
render_pass_begin_info.clearValueCount = 0;
render_pass_begin_info.pClearValues = nullptr;
vkCmdBeginRenderPass(cmd_buffer_, &render_pass_begin_info,
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
dfn.vkCmdBeginRenderPass(cmd_buffer_, &render_pass_begin_info,
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
// Render commands.
vkCmdExecuteCommands(cmd_buffer_, 1, &render_cmd_buffer_);
dfn.vkCmdExecuteCommands(cmd_buffer_, 1, &render_cmd_buffer_);
// End render pass.
vkCmdEndRenderPass(cmd_buffer_);
dfn.vkCmdEndRenderPass(cmd_buffer_);
// Transition the image to a format the presentation engine can source from.
// FIXME: Do we need more synchronization here between the copy buffer?
@@ -715,14 +728,14 @@ VkResult VulkanSwapChain::End() {
post_image_memory_barrier.subresourceRange.levelCount = 1;
post_image_memory_barrier.subresourceRange.baseArrayLayer = 0;
post_image_memory_barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmd_buffer_,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &post_image_memory_barrier);
dfn.vkCmdPipelineBarrier(cmd_buffer_,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &post_image_memory_barrier);
current_buffer.image_layout = post_image_memory_barrier.newLayout;
status = vkEndCommandBuffer(cmd_buffer_);
status = dfn.vkEndCommandBuffer(cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
@@ -752,8 +765,8 @@ VkResult VulkanSwapChain::End() {
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueueSubmit(presentation_queue_, 1, &render_submit_info,
synchronization_fence_);
status = dfn.vkQueueSubmit(presentation_queue_, 1, &render_submit_info,
synchronization_fence_);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}
@@ -777,7 +790,7 @@ VkResult VulkanSwapChain::End() {
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueuePresentKHR(presentation_queue_, &present_info);
status = dfn.vkQueuePresentKHR(presentation_queue_, &present_info);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}

View File

@@ -26,39 +26,37 @@ namespace xe {
namespace ui {
namespace vulkan {
#define VK_SAFE_DESTROY(fn, dev, obj, alloc) \
\
do { \
if (obj) { \
fn(dev, obj, alloc); \
obj = nullptr; \
} \
\
} while (0)
class Fence {
public:
Fence(VkDevice device) : device_(device) {
VkFenceCreateInfo fence_info;
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.pNext = nullptr;
fence_info.flags = 0;
vkCreateFence(device, &fence_info, nullptr, &fence_);
template <typename F, typename T>
inline bool DestroyAndNullHandle(F* destroy_function, T& handle) {
if (handle != VK_NULL_HANDLE) {
destroy_function(handle, nullptr);
handle = VK_NULL_HANDLE;
return true;
}
~Fence() {
vkDestroyFence(device_, fence_, nullptr);
fence_ = nullptr;
return false;
}
template <typename F, typename T>
inline bool DestroyAndNullHandle(F* destroy_function, VkInstance parent,
T& handle) {
if (handle != VK_NULL_HANDLE) {
destroy_function(parent, handle, nullptr);
handle = VK_NULL_HANDLE;
return true;
}
return false;
}
VkResult status() const { return vkGetFenceStatus(device_, fence_); }
VkFence fence() const { return fence_; }
operator VkFence() const { return fence_; }
private:
VkDevice device_;
VkFence fence_ = nullptr;
};
template <typename F, typename T>
inline bool DestroyAndNullHandle(F* destroy_function, VkDevice parent,
T& handle) {
if (handle != VK_NULL_HANDLE) {
destroy_function(parent, handle, nullptr);
handle = VK_NULL_HANDLE;
return true;
}
return false;
}
struct Version {
uint32_t major;