[Vulkan] Remove old Vulkan code, change shaders directory, create empty Vulkan backend

This commit is contained in:
Triang3l
2020-08-31 21:44:29 +03:00
parent 1e9ee8f43b
commit 7b93670dbd
461 changed files with 161 additions and 22194 deletions

View File

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

View File

@@ -1,101 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_BLITTER_H_
#define XENIA_UI_VULKAN_BLITTER_H_
#include <map>
#include <memory>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
namespace xe {
namespace ui {
namespace vulkan {
class DescriptorPool;
class Blitter {
public:
Blitter();
~Blitter();
VkResult Initialize(VulkanDevice* device);
void Scavenge();
void Shutdown();
// Queues commands to blit a texture to another texture.
//
// src_rect is the rectangle of pixels to copy from the source
// src_extents is the actual size of the source image
// dst_rect is the rectangle of pixels that are replaced with the source
// dst_extents is the actual size of the destination image
// dst_framebuffer must only have one attachment, the target texture.
// viewport is the viewport rect (set to {0, 0, dst_w, dst_h} if unsure)
// scissor is the scissor rect for the dest (set to dst size if unsure)
void BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
VkImageView src_image_view, VkRect2D src_rect,
VkExtent2D src_extents, VkFormat dst_image_format,
VkRect2D dst_rect, VkExtent2D dst_extents,
VkFramebuffer dst_framebuffer, VkViewport viewport,
VkRect2D scissor, VkFilter filter, bool color_or_depth,
bool swap_channels);
void CopyColorTexture2D(VkCommandBuffer command_buffer, VkFence fence,
VkImage src_image, VkImageView src_image_view,
VkOffset2D src_offset, VkImage dst_image,
VkImageView dst_image_view, VkExtent2D extents,
VkFilter filter, bool swap_channels);
void CopyDepthTexture(VkCommandBuffer command_buffer, VkFence fence,
VkImage src_image, VkImageView src_image_view,
VkOffset2D src_offset, VkImage dst_image,
VkImageView dst_image_view, VkExtent2D extents);
// For framebuffer creation.
VkRenderPass GetRenderPass(VkFormat format, bool color_or_depth);
private:
struct VtxPushConstants {
float src_uv[4]; // 0x00
float dst_uv[4]; // 0x10
};
struct PixPushConstants {
int _pad[3]; // 0x20
int swap; // 0x2C
};
VkPipeline GetPipeline(VkRenderPass render_pass, VkShaderModule frag_shader,
bool color_or_depth);
VkRenderPass CreateRenderPass(VkFormat output_format, bool color_or_depth);
VkPipeline CreatePipeline(VkRenderPass render_pass,
VkShaderModule frag_shader, bool color_or_depth);
std::unique_ptr<DescriptorPool> descriptor_pool_ = nullptr;
VulkanDevice* device_ = nullptr;
VkPipeline pipeline_color_ = nullptr;
VkPipeline pipeline_depth_ = nullptr;
VkPipelineLayout pipeline_layout_ = nullptr;
VkShaderModule blit_vertex_ = nullptr;
VkShaderModule blit_color_ = nullptr;
VkShaderModule blit_depth_ = nullptr;
VkSampler samp_linear_ = nullptr;
VkSampler samp_nearest_ = nullptr;
VkDescriptorSetLayout descriptor_set_layout_ = nullptr;
std::map<VkFormat, VkRenderPass> render_passes_;
std::map<std::pair<VkRenderPass, VkShaderModule>, VkPipeline> pipelines_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_BLITTER_H_

View File

@@ -1,281 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <algorithm>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/circular_buffer.h"
namespace xe {
namespace ui {
namespace vulkan {
CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
VkDeviceSize capacity, VkDeviceSize alignment)
: device_(device), capacity_(capacity) {
VkResult status = VK_SUCCESS;
// Create our internal buffer.
VkBufferCreateInfo buffer_info;
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.pNext = nullptr;
buffer_info.flags = 0;
buffer_info.size = capacity;
buffer_info.usage = usage;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.queueFamilyIndexCount = 0;
buffer_info.pQueueFamilyIndices = nullptr;
status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
CheckResult(status, "vkCreateBuffer");
if (status != VK_SUCCESS) {
assert_always();
}
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
alignment_ = xe::round_up(alignment, reqs.alignment);
}
CircularBuffer::~CircularBuffer() { Shutdown(); }
VkResult CircularBuffer::Initialize(VkDeviceMemory memory,
VkDeviceSize offset) {
assert_true(offset % alignment_ == 0);
gpu_memory_ = memory;
gpu_base_ = offset;
VkResult status = VK_SUCCESS;
// Bind the buffer to its backing memory.
status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
CheckResult(status, "vkBindBufferMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to bind memory!");
Shutdown();
return status;
}
// Map the memory so we can access it.
status = 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!");
Shutdown();
return status;
}
return VK_SUCCESS;
}
VkResult CircularBuffer::Initialize() {
VkResult status = VK_SUCCESS;
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
// Allocate memory from the device to back the buffer.
owns_gpu_memory_ = true;
gpu_memory_ = device_->AllocateMemory(reqs);
if (!gpu_memory_) {
XELOGE("CircularBuffer::Initialize - Failed to allocate memory!");
Shutdown();
return VK_ERROR_INITIALIZATION_FAILED;
}
capacity_ = reqs.size;
gpu_base_ = 0;
// Bind the buffer to its backing memory.
status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
CheckResult(status, "vkBindBufferMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to bind memory!");
Shutdown();
return status;
}
// Map the memory so we can access it.
status = 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!");
Shutdown();
return status;
}
return VK_SUCCESS;
}
void CircularBuffer::Shutdown() {
Clear();
if (host_base_) {
vkUnmapMemory(*device_, gpu_memory_);
host_base_ = nullptr;
}
if (gpu_buffer_) {
vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
gpu_buffer_ = nullptr;
}
if (gpu_memory_ && owns_gpu_memory_) {
vkFreeMemory(*device_, gpu_memory_, nullptr);
gpu_memory_ = nullptr;
}
}
void CircularBuffer::GetBufferMemoryRequirements(VkMemoryRequirements* reqs) {
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs);
}
bool CircularBuffer::CanAcquire(VkDeviceSize length) {
// Make sure the length is aligned.
length = xe::round_up(length, alignment_);
if (allocations_.empty()) {
// Read head has caught up to write head (entire buffer available for write)
assert_true(read_head_ == write_head_);
return capacity_ >= length;
} else if (write_head_ < read_head_) {
// Write head wrapped around and is behind read head.
// | write |---- read ----|
return (read_head_ - write_head_) >= length;
} else if (write_head_ > read_head_) {
// Read head behind write head.
// 1. Check if there's enough room from write -> capacity
// | |---- read ----| write |
if ((capacity_ - write_head_) >= length) {
return true;
}
// 2. Check if there's enough room from 0 -> read
// | write |---- read ----| |
if ((read_head_ - 0) >= length) {
return true;
}
}
return false;
}
CircularBuffer::Allocation* CircularBuffer::Acquire(VkDeviceSize length,
VkFence fence) {
VkDeviceSize aligned_length = xe::round_up(length, alignment_);
if (!CanAcquire(aligned_length)) {
return nullptr;
}
assert_true(write_head_ % alignment_ == 0);
if (write_head_ < read_head_) {
// Write head behind read head.
assert_true(read_head_ - write_head_ >= aligned_length);
Allocation alloc;
alloc.host_ptr = host_base_ + write_head_;
alloc.gpu_memory = gpu_memory_;
alloc.offset = gpu_base_ + write_head_;
alloc.length = length;
alloc.aligned_length = aligned_length;
alloc.fence = fence;
write_head_ += aligned_length;
allocations_.push(alloc);
return &allocations_.back();
} else {
// Write head equal to/after read head
if (capacity_ - write_head_ >= aligned_length) {
// Free space from write -> capacity
Allocation alloc;
alloc.host_ptr = host_base_ + write_head_;
alloc.gpu_memory = gpu_memory_;
alloc.offset = gpu_base_ + write_head_;
alloc.length = length;
alloc.aligned_length = aligned_length;
alloc.fence = fence;
write_head_ += aligned_length;
allocations_.push(alloc);
return &allocations_.back();
} else if ((read_head_ - 0) >= aligned_length) {
// Not enough space from write -> capacity, but there is enough free space
// from begin -> read
Allocation alloc;
alloc.host_ptr = host_base_ + 0;
alloc.gpu_memory = gpu_memory_;
alloc.offset = gpu_base_ + 0;
alloc.length = length;
alloc.aligned_length = aligned_length;
alloc.fence = fence;
write_head_ = aligned_length;
allocations_.push(alloc);
return &allocations_.back();
}
}
return nullptr;
}
void CircularBuffer::Flush(Allocation* allocation) {
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);
}
void CircularBuffer::Flush(VkDeviceSize offset, VkDeviceSize length) {
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);
}
void CircularBuffer::Clear() {
allocations_ = std::queue<Allocation>{};
write_head_ = read_head_ = 0;
}
void CircularBuffer::Scavenge() {
// 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) {
// Don't bother freeing following allocations to ensure proper ordering.
break;
}
fence = alloc.fence;
if (capacity_ - read_head_ < alloc.aligned_length) {
// This allocation is stored at the beginning of the buffer.
read_head_ = alloc.aligned_length;
} else {
read_head_ += alloc.aligned_length;
}
allocations_.pop();
}
if (allocations_.empty()) {
// Reset R/W heads to work around fragmentation issues.
read_head_ = write_head_ = 0;
}
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,93 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_CIRCULAR_BUFFER_H_
#define XENIA_UI_VULKAN_CIRCULAR_BUFFER_H_
#include <queue>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
namespace xe {
namespace ui {
namespace vulkan {
// A circular buffer, intended to hold (fairly) temporary memory that will be
// released when a fence is signaled. Best used when allocations are taken
// in-order with command buffer submission.
//
// Allocations loop around the buffer in circles (but are not fragmented at the
// ends of the buffer), where trailing older allocations are freed after use.
class CircularBuffer {
public:
CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
VkDeviceSize capacity, VkDeviceSize alignment = 256);
~CircularBuffer();
struct Allocation {
void* host_ptr;
VkDeviceMemory gpu_memory;
VkDeviceSize offset;
VkDeviceSize length;
VkDeviceSize aligned_length;
// Allocation usage fence. This allocation will be deleted when the fence
// becomes signaled.
VkFence fence;
};
VkResult Initialize(VkDeviceMemory memory, VkDeviceSize offset);
VkResult Initialize();
void Shutdown();
void GetBufferMemoryRequirements(VkMemoryRequirements* reqs);
VkDeviceSize alignment() const { return alignment_; }
VkDeviceSize capacity() const { return capacity_; }
VkBuffer gpu_buffer() const { return gpu_buffer_; }
VkDeviceMemory gpu_memory() const { return gpu_memory_; }
uint8_t* host_base() const { return host_base_; }
bool CanAcquire(VkDeviceSize length);
// Acquires space to hold memory. This allocation is only freed when the fence
// reaches the signaled state.
Allocation* Acquire(VkDeviceSize length, VkFence fence);
void Flush(Allocation* allocation);
void Flush(VkDeviceSize offset, VkDeviceSize length);
// Clears all allocations, regardless of whether they've been consumed or not.
void Clear();
// Frees any allocations whose fences have been signaled.
void Scavenge();
private:
// All of these variables are relative to gpu_base
VkDeviceSize capacity_ = 0;
VkDeviceSize alignment_ = 0;
VkDeviceSize write_head_ = 0;
VkDeviceSize read_head_ = 0;
VulkanDevice* device_;
bool owns_gpu_memory_ = false;
VkBuffer gpu_buffer_ = nullptr;
VkDeviceMemory gpu_memory_ = nullptr;
VkDeviceSize gpu_base_ = 0;
uint8_t* host_base_ = nullptr;
std::queue<Allocation> allocations_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_CIRCULAR_BUFFER_H_

View File

@@ -1,124 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/fenced_pools.h"
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
using xe::ui::vulkan::CheckResult;
CommandBufferPool::CommandBufferPool(VkDevice device,
uint32_t queue_family_index)
: BaseFencedPool(device) {
// Create the pool used for allocating buffers.
// They are marked as transient (short-lived) and cycled frequently.
VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = nullptr;
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
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_);
CheckResult(err, "vkCreateCommandPool");
// Allocate a bunch of command buffers to start.
constexpr uint32_t kDefaultCount = 32;
VkCommandBufferAllocateInfo command_buffer_info;
command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_info.pNext = nullptr;
command_buffer_info.commandPool = command_pool_;
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);
CheckResult(err, "vkCreateCommandBuffer");
for (size_t i = 0; i < xe::countof(command_buffers); ++i) {
PushEntry(command_buffers[i], nullptr);
}
}
CommandBufferPool::~CommandBufferPool() {
FreeAllEntries();
vkDestroyCommandPool(device_, command_pool_, nullptr);
command_pool_ = nullptr;
}
VkCommandBuffer CommandBufferPool::AllocateEntry(void* data) {
// TODO(benvanik): allocate a bunch at once?
VkCommandBufferAllocateInfo command_buffer_info;
command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_info.pNext = nullptr;
command_buffer_info.commandPool = command_pool_;
command_buffer_info.level =
VkCommandBufferLevel(reinterpret_cast<uintptr_t>(data));
command_buffer_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
auto err =
vkAllocateCommandBuffers(device_, &command_buffer_info, &command_buffer);
CheckResult(err, "vkCreateCommandBuffer");
return command_buffer;
}
void CommandBufferPool::FreeEntry(VkCommandBuffer handle) {
vkFreeCommandBuffers(device_, command_pool_, 1, &handle);
}
DescriptorPool::DescriptorPool(VkDevice device, uint32_t max_count,
std::vector<VkDescriptorPoolSize> pool_sizes)
: BaseFencedPool(device) {
VkDescriptorPoolCreateInfo descriptor_pool_info;
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptor_pool_info.pNext = nullptr;
descriptor_pool_info.flags =
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
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_);
CheckResult(err, "vkCreateDescriptorPool");
}
DescriptorPool::~DescriptorPool() {
FreeAllEntries();
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
descriptor_pool_ = nullptr;
}
VkDescriptorSet DescriptorPool::AllocateEntry(void* data) {
VkDescriptorSetLayout layout = reinterpret_cast<VkDescriptorSetLayout>(data);
VkDescriptorSet descriptor_set = nullptr;
VkDescriptorSetAllocateInfo set_alloc_info;
set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
set_alloc_info.pNext = nullptr;
set_alloc_info.descriptorPool = descriptor_pool_;
set_alloc_info.descriptorSetCount = 1;
set_alloc_info.pSetLayouts = &layout;
auto err =
vkAllocateDescriptorSets(device_, &set_alloc_info, &descriptor_set);
CheckResult(err, "vkAllocateDescriptorSets");
return descriptor_set;
}
void DescriptorPool::FreeEntry(VkDescriptorSet handle) {
vkFreeDescriptorSets(device_, descriptor_pool_, 1, &handle);
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,334 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_FENCED_POOLS_H_
#define XENIA_UI_VULKAN_FENCED_POOLS_H_
#include <memory>
#include "xenia/base/assert.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
// Simple pool for Vulkan homogenous objects that cannot be reused while
// in-flight.
// It batches pooled objects into groups and uses a vkQueueSubmit fence to
// indicate their availability. If no objects are free when one is requested
// the caller is expected to create them.
template <typename T, typename HANDLE>
class BaseFencedPool {
public:
BaseFencedPool(VkDevice device) : device_(device) {}
virtual ~BaseFencedPool() {
// TODO(benvanik): wait on fence until done.
assert_null(pending_batch_list_head_);
// Subclasses must call FreeAllEntries() to properly clean up things.
assert_null(free_batch_list_head_);
assert_null(free_entry_list_head_);
}
// True if one or more batches are still pending on the GPU.
bool has_pending() const { return pending_batch_list_head_ != nullptr; }
// True if a batch is open.
bool has_open_batch() const { return open_batch_ != nullptr; }
// Checks all pending batches for completion and scavenges their entries.
// This should be called as frequently as reasonable.
void Scavenge() {
while (pending_batch_list_head_) {
auto batch = pending_batch_list_head_;
assert_not_null(batch->fence);
VkResult status = vkGetFenceStatus(device_, batch->fence);
if (status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST) {
// Batch has completed. Reclaim.
pending_batch_list_head_ = batch->next;
if (batch == pending_batch_list_tail_) {
pending_batch_list_tail_ = nullptr;
}
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
batch->entry_list_tail->next = free_entry_list_head_;
free_entry_list_head_ = batch->entry_list_head;
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
} else {
// Batch is still in-flight. Since batches are executed in order we know
// no others after it could have completed, so early-exit.
return;
}
}
}
// Begins a new batch.
// All entries acquired within this batch will be marked as in-use until
// the fence returned is signalled.
// Pass in a fence to use an external fence. This assumes the fence has been
// reset.
VkFence BeginBatch(VkFence fence = nullptr) {
assert_null(open_batch_);
Batch* batch = nullptr;
if (free_batch_list_head_) {
// Reuse a batch.
batch = free_batch_list_head_;
free_batch_list_head_ = batch->next;
batch->next = nullptr;
if (batch->flags & kBatchOwnsFence && !fence) {
// Reset owned fence.
vkResetFences(device_, 1, &batch->fence);
} else if ((batch->flags & kBatchOwnsFence) && fence) {
// Transfer owned -> external
vkDestroyFence(device_, batch->fence, nullptr);
batch->fence = fence;
batch->flags &= ~kBatchOwnsFence;
} else if (!(batch->flags & kBatchOwnsFence) && !fence) {
// external -> owned
VkFenceCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
VkResult res = vkCreateFence(device_, &info, nullptr, &batch->fence);
if (res != VK_SUCCESS) {
assert_always();
}
batch->flags |= kBatchOwnsFence;
} else {
// external -> external
batch->fence = fence;
}
} else {
// Allocate new batch.
batch = new Batch();
batch->next = nullptr;
batch->flags = 0;
if (!fence) {
VkFenceCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
VkResult res = vkCreateFence(device_, &info, nullptr, &batch->fence);
if (res != VK_SUCCESS) {
assert_always();
}
batch->flags |= kBatchOwnsFence;
} else {
batch->fence = fence;
}
}
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
open_batch_ = batch;
return batch->fence;
}
// Cancels an open batch, and releases all entries acquired within.
void CancelBatch() {
assert_not_null(open_batch_);
auto batch = open_batch_;
open_batch_ = nullptr;
// Relink the batch back into the free batch list.
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
// Relink entries back into free entries list.
batch->entry_list_tail->next = free_entry_list_head_;
free_entry_list_head_ = batch->entry_list_head;
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
}
// Ends the current batch.
void EndBatch() {
assert_not_null(open_batch_);
// Close and see if we have anything.
auto batch = open_batch_;
open_batch_ = nullptr;
if (!batch->entry_list_head) {
// Nothing to do.
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
return;
}
// Append to the end of the batch list.
batch->next = nullptr;
if (!pending_batch_list_head_) {
pending_batch_list_head_ = batch;
}
if (pending_batch_list_tail_) {
pending_batch_list_tail_->next = batch;
pending_batch_list_tail_ = batch;
} else {
pending_batch_list_tail_ = batch;
}
}
protected:
// Attempts to acquire an entry from the pool in the current batch.
// If none are available a new one will be allocated.
HANDLE AcquireEntry(void* data) {
Entry* entry = nullptr;
if (free_entry_list_head_) {
// Slice off an entry from the free list.
Entry* prev = nullptr;
Entry* cur = free_entry_list_head_;
while (cur != nullptr) {
if (cur->data == data) {
if (prev) {
prev->next = cur->next;
} else {
free_entry_list_head_ = cur->next;
}
entry = cur;
break;
}
prev = cur;
cur = cur->next;
}
}
if (!entry) {
// No entry available; allocate new.
entry = new Entry();
entry->data = data;
entry->handle = static_cast<T*>(this)->AllocateEntry(data);
if (!entry->handle) {
delete entry;
return nullptr;
}
}
entry->next = nullptr;
if (!open_batch_->entry_list_head) {
open_batch_->entry_list_head = entry;
}
if (open_batch_->entry_list_tail) {
open_batch_->entry_list_tail->next = entry;
}
open_batch_->entry_list_tail = entry;
return entry->handle;
}
void PushEntry(HANDLE handle, void* data) {
auto entry = new Entry();
entry->next = free_entry_list_head_;
entry->data = data;
entry->handle = handle;
free_entry_list_head_ = entry;
}
void FreeAllEntries() {
// 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);
batch->fence = nullptr;
}
delete batch;
}
while (free_entry_list_head_) {
auto entry = free_entry_list_head_;
free_entry_list_head_ = entry->next;
static_cast<T*>(this)->FreeEntry(entry->handle);
delete entry;
}
}
VkDevice device_ = nullptr;
private:
struct Entry {
Entry* next;
void* data;
HANDLE handle;
};
struct Batch {
Batch* next;
Entry* entry_list_head;
Entry* entry_list_tail;
uint32_t flags;
VkFence fence;
};
static const uint32_t kBatchOwnsFence = 1;
Batch* free_batch_list_head_ = nullptr;
Entry* free_entry_list_head_ = nullptr;
Batch* pending_batch_list_head_ = nullptr;
Batch* pending_batch_list_tail_ = nullptr;
Batch* open_batch_ = nullptr;
};
class CommandBufferPool
: public BaseFencedPool<CommandBufferPool, VkCommandBuffer> {
public:
typedef BaseFencedPool<CommandBufferPool, VkCommandBuffer> Base;
CommandBufferPool(VkDevice device, uint32_t queue_family_index);
~CommandBufferPool() override;
VkCommandBuffer AcquireEntry(
VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) {
return Base::AcquireEntry(reinterpret_cast<void*>(level));
}
protected:
friend class BaseFencedPool<CommandBufferPool, VkCommandBuffer>;
VkCommandBuffer AllocateEntry(void* data);
void FreeEntry(VkCommandBuffer handle);
VkCommandPool command_pool_ = nullptr;
};
class DescriptorPool : public BaseFencedPool<DescriptorPool, VkDescriptorSet> {
public:
typedef BaseFencedPool<DescriptorPool, VkDescriptorSet> Base;
DescriptorPool(VkDevice device, uint32_t max_count,
std::vector<VkDescriptorPoolSize> pool_sizes);
~DescriptorPool() override;
VkDescriptorSet AcquireEntry(VkDescriptorSetLayout layout) {
return Base::AcquireEntry(layout);
}
// WARNING: Allocating sets from the vulkan pool will not be tracked!
VkDescriptorPool descriptor_pool() { return descriptor_pool_; }
protected:
friend class BaseFencedPool<DescriptorPool, VkDescriptorSet>;
VkDescriptorSet AllocateEntry(void* data);
void FreeEntry(VkDescriptorSet handle);
VkDescriptorPool descriptor_pool_ = nullptr;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_FENCED_POOLS_H_

View File

@@ -7,55 +7,10 @@ project("xenia-ui-vulkan")
kind("StaticLib")
language("C++")
links({
"fmt",
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
})
defines({
})
includedirs({
project_root.."/third_party/vulkan/",
})
local_platform_files()
files({
"shaders/bin/*.h",
"../shaders/bytecode/vulkan_spirv/*.h",
})
removefiles({"*_demo.cc"})
group("demos")
project("xenia-ui-window-vulkan-demo")
uuid("97598f13-3177-454c-8e58-c59e2b6ede27")
kind("WindowedApp")
language("C++")
links({
"fmt",
"imgui",
"volk",
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
})
defines({
})
includedirs({
project_root.."/third_party/vulkan/",
})
files({
"../window_demo.cc",
"vulkan_window_demo.cc",
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
})
resincludedirs({
project_root,
})
filter("platforms:Linux")
links({
"X11",
"xcb",
"X11-xcb",
"GL",
"vulkan",
})

View File

@@ -1,88 +0,0 @@
// generated from `xb genspirv`
// source: blit_color.frag
const uint8_t blit_color_frag[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6F, 0x43, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x5F,
0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x50, 0x75, 0x73, 0x68,
0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00,
0x06, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5F, 0x70, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00,
0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x77, 0x61, 0x70,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
0x70, 0x75, 0x73, 0x68, 0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E,
0x74, 0x73, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x07, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};

View File

@@ -1,67 +0,0 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 6
; Bound: 36
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %oC %vtx_uv
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %oC "oC"
OpName %src_texture "src_texture"
OpName %vtx_uv "vtx_uv"
OpName %PushConstants "PushConstants"
OpMemberName %PushConstants 0 "_pad"
OpMemberName %PushConstants 1 "swap"
OpName %push_constants "push_constants"
OpDecorate %oC Location 0
OpDecorate %src_texture DescriptorSet 0
OpDecorate %src_texture Binding 0
OpDecorate %vtx_uv Location 0
OpMemberDecorate %PushConstants 0 Offset 32
OpMemberDecorate %PushConstants 1 Offset 44
OpDecorate %PushConstants Block
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%oC = OpVariable %_ptr_Output_v4float Output
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %10
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%src_texture = OpVariable %_ptr_UniformConstant_11 UniformConstant
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%vtx_uv = OpVariable %_ptr_Input_v2float Input
%v3float = OpTypeVector %float 3
%int = OpTypeInt 32 1
%PushConstants = OpTypeStruct %v3float %int
%_ptr_PushConstant_PushConstants = OpTypePointer PushConstant %PushConstants
%push_constants = OpVariable %_ptr_PushConstant_PushConstants PushConstant
%int_1 = OpConstant %int 1
%_ptr_PushConstant_int = OpTypePointer PushConstant %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%main = OpFunction %void None %3
%5 = OpLabel
%14 = OpLoad %11 %src_texture
%18 = OpLoad %v2float %vtx_uv
%19 = OpImageSampleImplicitLod %v4float %14 %18
OpStore %oC %19
%27 = OpAccessChain %_ptr_PushConstant_int %push_constants %int_1
%28 = OpLoad %int %27
%31 = OpINotEqual %bool %28 %int_0
OpSelectionMerge %33 None
OpBranchConditional %31 %32 %33
%32 = OpLabel
%34 = OpLoad %v4float %oC
%35 = OpVectorShuffle %v4float %34 %34 2 1 0 3
OpStore %oC %35
OpBranch %33
%33 = OpLabel
OpReturn
OpFunctionEnd

View File

@@ -1,59 +0,0 @@
// generated from `xb genspirv`
// source: blit_depth.frag
const uint8_t blit_depth_frag[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00,
0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F,
0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x6F, 0x43, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x57, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

View File

@@ -1,46 +0,0 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 6
; Bound: 30
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %gl_FragDepth %vtx_uv %oC
OpExecutionMode %main OriginUpperLeft
OpExecutionMode %main DepthReplacing
OpSource GLSL 450
OpName %main "main"
OpName %gl_FragDepth "gl_FragDepth"
OpName %src_texture "src_texture"
OpName %vtx_uv "vtx_uv"
OpName %oC "oC"
OpDecorate %gl_FragDepth BuiltIn FragDepth
OpDecorate %src_texture DescriptorSet 0
OpDecorate %src_texture Binding 0
OpDecorate %vtx_uv Location 0
OpDecorate %oC Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Output_float = OpTypePointer Output %float
%gl_FragDepth = OpVariable %_ptr_Output_float Output
%9 = OpTypeImage %float 2D 0 0 0 1 Unknown
%10 = OpTypeSampledImage %9
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%src_texture = OpVariable %_ptr_UniformConstant_10 UniformConstant
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%vtx_uv = OpVariable %_ptr_Input_v2float Input
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%oC = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %3
%5 = OpLabel
%13 = OpLoad %10 %src_texture
%17 = OpLoad %v2float %vtx_uv
%19 = OpImageSampleImplicitLod %v4float %13 %17
%22 = OpCompositeExtract %float %19 0
OpStore %gl_FragDepth %22
OpReturn
OpFunctionEnd

View File

@@ -1,149 +0,0 @@
// generated from `xb genspirv`
// source: blit.vert
const uint8_t blit_vert[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00,
0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6E, 0x64,
0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00,
0x69, 0x6E, 0x64, 0x65, 0x78, 0x61, 0x62, 0x6C, 0x65, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x25, 0x00, 0x00, 0x00, 0x50, 0x75, 0x73, 0x68,
0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00,
0x06, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00,
0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x5F,
0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00,
0x70, 0x75, 0x73, 0x68, 0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E,
0x74, 0x73, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x30, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74,
0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x30, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74,
0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00,
0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43,
0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00,
0x06, 0x00, 0x07, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00,
0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x16, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x07, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x17, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x26, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x07, 0x00, 0x22, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x06, 0x00, 0x30, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x22, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x85, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x35, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00,
0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x40, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x29, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x42, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};

View File

@@ -1,99 +0,0 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 6
; Bound: 76
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %gl_VertexIndex %_ %vtx_uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_VertexIndex "gl_VertexIndex"
OpName %indexable "indexable"
OpName %PushConstants "PushConstants"
OpMemberName %PushConstants 0 "src_uv"
OpMemberName %PushConstants 1 "dst_uv"
OpName %push_constants "push_constants"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpMemberName %gl_PerVertex 1 "gl_PointSize"
OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
OpMemberName %gl_PerVertex 3 "gl_CullDistance"
OpName %_ ""
OpName %vtx_uv "vtx_uv"
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %PushConstants 0 Offset 0
OpMemberDecorate %PushConstants 1 Offset 16
OpDecorate %PushConstants Block
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
OpDecorate %gl_PerVertex Block
OpDecorate %vtx_uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%uint = OpTypeInt 32 0
%uint_4 = OpConstant %uint 4
%_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4
%float_0 = OpConstant %float 0
%14 = OpConstantComposite %v2float %float_0 %float_0
%float_1 = OpConstant %float 1
%16 = OpConstantComposite %v2float %float_1 %float_0
%17 = OpConstantComposite %v2float %float_0 %float_1
%18 = OpConstantComposite %v2float %float_1 %float_1
%19 = OpConstantComposite %_arr_v2float_uint_4 %14 %16 %17 %18
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_ptr_Function__arr_v2float_uint_4 = OpTypePointer Function %_arr_v2float_uint_4
%float_2 = OpConstant %float 2
%v4float = OpTypeVector %float 4
%PushConstants = OpTypeStruct %v4float %v4float
%_ptr_PushConstant_PushConstants = OpTypePointer PushConstant %PushConstants
%push_constants = OpVariable %_ptr_PushConstant_PushConstants PushConstant
%int_1 = OpConstant %int 1
%_ptr_PushConstant_v4float = OpTypePointer PushConstant %v4float
%44 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
%uint_1 = OpConstant %uint 1
%_arr_float_uint_1 = OpTypeArray %float %uint_1
%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int_0 = OpConstant %int 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Output_v2float = OpTypePointer Output %v2float
%vtx_uv = OpVariable %_ptr_Output_v2float Output
%main = OpFunction %void None %3
%5 = OpLabel
%indexable = OpVariable %_ptr_Function__arr_v2float_uint_4 Function
%23 = OpLoad %int %gl_VertexIndex
OpStore %indexable %19
%26 = OpAccessChain %_ptr_Function_v2float %indexable %23
%27 = OpLoad %v2float %26
%42 = OpAccessChain %_ptr_PushConstant_v4float %push_constants %int_1
%43 = OpLoad %v4float %42
%45 = OpFMul %v4float %43 %44
%53 = OpVectorShuffle %v2float %45 %45 0 1
%54 = OpFSub %v2float %53 %18
%57 = OpVectorShuffle %v2float %45 %45 2 3
%58 = OpFMul %v2float %27 %57
%59 = OpFAdd %v2float %54 %58
%60 = OpCompositeExtract %float %59 0
%61 = OpCompositeExtract %float %59 1
%62 = OpCompositeConstruct %v4float %60 %61 %float_0 %float_1
%64 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %64 %62
%68 = OpAccessChain %_ptr_PushConstant_v4float %push_constants %int_0
%69 = OpLoad %v4float %68
%70 = OpVectorShuffle %v2float %69 %69 2 3
%71 = OpFMul %v2float %27 %70
%74 = OpVectorShuffle %v2float %69 %69 0 1
%75 = OpFAdd %v2float %71 %74
OpStore %vtx_uv %75
OpReturn
OpFunctionEnd

View File

@@ -1,109 +0,0 @@
// generated from `xb genspirv`
// source: immediate.frag
const uint8_t immediate_frag[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00,
0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F,
0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, 0x63, 0x6F, 0x6C, 0x6F,
0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74,
0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74,
0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x61, 0x6D,
0x70, 0x6C, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x11, 0x00, 0x00, 0x00, 0x70, 0x75, 0x73, 0x68, 0x5F, 0x63, 0x6F, 0x6E,
0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74,
0x75, 0x72, 0x65, 0x5F, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x00,
0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x3F, 0x19, 0x00, 0x09, 0x00, 0x29, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x29, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};

View File

@@ -1,83 +0,0 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 6
; Bound: 51
; Schema: 0
OpCapability Shader
OpCapability Sampled1D
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %out_color %vtx_color %vtx_uv
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %out_color "out_color"
OpName %vtx_color "vtx_color"
OpName %PushConstants "PushConstants"
OpMemberName %PushConstants 0 "restrict_texture_samples"
OpName %push_constants "push_constants"
OpName %vtx_uv "vtx_uv"
OpName %texture_sampler "texture_sampler"
OpDecorate %out_color Location 0
OpDecorate %vtx_color Location 1
OpMemberDecorate %PushConstants 0 Offset 64
OpDecorate %PushConstants Block
OpDecorate %vtx_uv Location 0
OpDecorate %texture_sampler DescriptorSet 0
OpDecorate %texture_sampler Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%out_color = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%vtx_color = OpVariable %_ptr_Input_v4float Input
%bool = OpTypeBool
%int = OpTypeInt 32 1
%PushConstants = OpTypeStruct %int
%_ptr_PushConstant_PushConstants = OpTypePointer PushConstant %PushConstants
%push_constants = OpVariable %_ptr_PushConstant_PushConstants PushConstant
%int_0 = OpConstant %int 0
%_ptr_PushConstant_int = OpTypePointer PushConstant %int
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%vtx_uv = OpVariable %_ptr_Input_v2float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_1 = OpConstant %float 1
%41 = OpTypeImage %float 2D 0 0 0 1 Unknown
%42 = OpTypeSampledImage %41
%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42
%texture_sampler = OpVariable %_ptr_UniformConstant_42 UniformConstant
%main = OpFunction %void None %3
%5 = OpLabel
%12 = OpLoad %v4float %vtx_color
OpStore %out_color %12
%20 = OpAccessChain %_ptr_PushConstant_int %push_constants %int_0
%21 = OpLoad %int %20
%22 = OpIEqual %bool %21 %int_0
%23 = OpLogicalNot %bool %22
OpSelectionMerge %25 None
OpBranchConditional %23 %24 %25
%24 = OpLabel
%32 = OpAccessChain %_ptr_Input_float %vtx_uv %uint_0
%33 = OpLoad %float %32
%35 = OpFOrdLessThanEqual %bool %33 %float_1
OpBranch %25
%25 = OpLabel
%36 = OpPhi %bool %22 %5 %35 %24
OpSelectionMerge %38 None
OpBranchConditional %36 %37 %38
%37 = OpLabel
%45 = OpLoad %42 %texture_sampler
%46 = OpLoad %v2float %vtx_uv
%47 = OpImageSampleImplicitLod %v4float %45 %46
%49 = OpLoad %v4float %out_color
%50 = OpFMul %v4float %49 %47
OpStore %out_color %50
OpBranch %38
%38 = OpLabel
OpReturn
OpFunctionEnd

View File

@@ -1,128 +0,0 @@
// generated from `xb genspirv`
// source: immediate.vert
const uint8_t immediate_vert[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74,
0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74,
0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43,
0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00,
0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00,
0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74,
0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x11, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x69,
0x6F, 0x6E, 0x5F, 0x6D, 0x61, 0x74, 0x72, 0x69, 0x78, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x13, 0x00, 0x00, 0x00, 0x70, 0x75, 0x73, 0x68,
0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70,
0x6F, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00,
0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F,
0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00,
0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x24, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x17, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

View File

@@ -1,90 +0,0 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 6
; Bound: 48
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %in_pos %vtx_uv %in_uv %vtx_color %in_color
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpMemberName %gl_PerVertex 1 "gl_PointSize"
OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
OpMemberName %gl_PerVertex 3 "gl_CullDistance"
OpName %_ ""
OpName %PushConstants "PushConstants"
OpMemberName %PushConstants 0 "projection_matrix"
OpName %push_constants "push_constants"
OpName %in_pos "in_pos"
OpName %vtx_uv "vtx_uv"
OpName %in_uv "in_uv"
OpName %vtx_color "vtx_color"
OpName %in_color "in_color"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
OpDecorate %gl_PerVertex Block
OpMemberDecorate %PushConstants 0 ColMajor
OpMemberDecorate %PushConstants 0 Offset 0
OpMemberDecorate %PushConstants 0 MatrixStride 16
OpDecorate %PushConstants Block
OpDecorate %in_pos Location 0
OpDecorate %vtx_uv Location 0
OpDecorate %in_uv Location 1
OpDecorate %vtx_color Location 1
OpDecorate %in_color Location 2
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_arr_float_uint_1 = OpTypeArray %float %uint_1
%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%mat4v4float = OpTypeMatrix %v4float 4
%PushConstants = OpTypeStruct %mat4v4float
%_ptr_PushConstant_PushConstants = OpTypePointer PushConstant %PushConstants
%push_constants = OpVariable %_ptr_PushConstant_PushConstants PushConstant
%_ptr_PushConstant_mat4v4float = OpTypePointer PushConstant %mat4v4float
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%in_pos = OpVariable %_ptr_Input_v2float Input
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Output_float = OpTypePointer Output %float
%_ptr_Output_v2float = OpTypePointer Output %v2float
%vtx_uv = OpVariable %_ptr_Output_v2float Output
%in_uv = OpVariable %_ptr_Input_v2float Input
%vtx_color = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%in_color = OpVariable %_ptr_Input_v4float Input
%main = OpFunction %void None %3
%5 = OpLabel
%21 = OpAccessChain %_ptr_PushConstant_mat4v4float %push_constants %int_0
%22 = OpLoad %mat4v4float %21
%26 = OpLoad %v2float %in_pos
%29 = OpCompositeExtract %float %26 0
%30 = OpCompositeExtract %float %26 1
%31 = OpCompositeConstruct %v4float %29 %30 %float_0 %float_1
%32 = OpMatrixTimesVector %v4float %22 %31
%34 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %34 %32
%36 = OpAccessChain %_ptr_Output_float %_ %int_0 %uint_1
%37 = OpLoad %float %36
%38 = OpFNegate %float %37
OpStore %36 %38
%43 = OpLoad %v2float %in_uv
OpStore %vtx_uv %43
%47 = OpLoad %v4float %in_color
OpStore %vtx_color %47
OpReturn
OpFunctionEnd

View File

@@ -1,31 +0,0 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
// normalized [x, y, w, h]
layout(offset = 0x00) vec4 src_uv;
layout(offset = 0x10) vec4 dst_uv;
} push_constants;
layout(location = 0) out vec2 vtx_uv;
void main() {
const vec2 vtx_arr[4]=vec2[4](
vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1)
);
vec2 vfetch_pos = vtx_arr[gl_VertexIndex];
vec2 scaled_pos = vfetch_pos.xy * vec2(2.0, 2.0) - vec2(1.0, 1.0);
vec4 scaled_dst_uv = push_constants.dst_uv * vec4(2.0);
gl_Position =
vec4(scaled_dst_uv.xy - vec2(1.0) + vfetch_pos.xy * scaled_dst_uv.zw, 0.0,
1.0);
vtx_uv = vfetch_pos.xy * push_constants.src_uv.zw + push_constants.src_uv.xy;
}

View File

@@ -1,20 +0,0 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
layout(offset = 0x20) vec3 _pad;
layout(offset = 0x2C) int swap;
} push_constants;
layout(set = 0, binding = 0) uniform sampler2D src_texture;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
oC = texture(src_texture, vtx_uv);
if (push_constants.swap != 0) oC = oC.bgra;
}

View File

@@ -1,19 +0,0 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
layout(offset = 0x20) vec3 _pad;
layout(offset = 0x2C) int swap;
} push_constants;
layout(set = 0, binding = 0) uniform sampler2D src_texture;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
gl_FragDepth = texture(src_texture, vtx_uv).r;
}

View File

@@ -1,28 +0,0 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
layout(offset = 64) int restrict_texture_samples;
} push_constants;
layout(set = 0, binding = 0) uniform sampler2D texture_sampler;
layout(set = 0, binding = 1) uniform sampler1D tex1D;
layout(set = 0, binding = 1) uniform sampler2D tex2D;
layout(location = 0) in vec2 vtx_uv;
layout(location = 1) in vec4 vtx_color;
layout(location = 0) out vec4 out_color;
void main() {
out_color = vtx_color;
if (push_constants.restrict_texture_samples == 0 || vtx_uv.x <= 1.0) {
vec4 tex_color = texture(texture_sampler, vtx_uv);
out_color *= tex_color;
// TODO(benvanik): microprofiler shadows.
}
}

View File

@@ -1,23 +0,0 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
layout(offset = 0) mat4 projection_matrix;
} push_constants;
layout(location = 0) in vec2 in_pos;
layout(location = 1) in vec2 in_uv;
layout(location = 2) in vec4 in_color;
layout(location = 0) out vec2 vtx_uv;
layout(location = 1) out vec4 vtx_color;
void main() {
gl_Position = push_constants.projection_matrix * vec4(in_pos.xy, 0.0, 1.0);
gl_Position.y = -gl_Position.y;
vtx_uv = in_uv;
vtx_color = in_color;
}

View File

@@ -1,18 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan.h"
DEFINE_bool(vulkan_validation, false, "Enable Vulkan validation layers.",
"Vulkan");
DEFINE_bool(vulkan_primary_queue_only, false,
"Force the use of the primary queue, ignoring any additional that "
"may be present.",
"Vulkan");

View File

@@ -1,33 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_VULKAN_H_
#define XENIA_UI_VULKAN_VULKAN_H_
#include "xenia/base/platform.h"
#if XE_PLATFORM_WIN32
#define VK_USE_PLATFORM_WIN32_KHR 1
#elif XE_PLATFORM_LINUX
#define VK_USE_PLATFORM_XCB_KHR 1
#else
#error Platform not yet supported.
#endif // XE_PLATFORM_WIN32
// We use a loader with its own function prototypes.
#include "third_party/volk/volk.h"
#include "third_party/vulkan/vulkan.h"
#include "xenia/base/cvar.h"
#define XELOGVK XELOGI
DECLARE_bool(vulkan_validation);
DECLARE_bool(vulkan_primary_queue_only);
#endif // XENIA_UI_VULKAN_VULKAN_H_

View File

@@ -9,27 +9,8 @@
#include "xenia/ui/vulkan/vulkan_context.h"
#include <mutex>
#include <string>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/profiling.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_instance.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
#include "xenia/ui/vulkan/vulkan_swap_chain.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
#if XE_PLATFORM_LINUX
#include "xenia/ui/window_gtk.h"
#include <X11/Xlib-xcb.h>
#endif
namespace xe {
namespace ui {
@@ -38,164 +19,18 @@ namespace vulkan {
VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
: GraphicsContext(provider, target_window) {}
VulkanContext::~VulkanContext() {
VkResult status;
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
{
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
status = vkQueueWaitIdle(device->primary_queue());
}
immediate_drawer_.reset();
swap_chain_.reset();
}
bool VulkanContext::Initialize() {
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
if (target_window_) {
// Create swap chain used to present to the window.
VkResult status = VK_ERROR_FEATURE_NOT_PRESENT;
VkSurfaceKHR surface = nullptr;
#if XE_PLATFORM_WIN32
VkWin32SurfaceCreateInfoKHR create_info;
create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
create_info.pNext = nullptr;
create_info.flags = 0;
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);
CheckResult(status, "vkCreateWin32SurfaceKHR");
#elif XE_PLATFORM_LINUX
#ifdef GDK_WINDOWING_X11
GtkWidget* window_handle =
static_cast<GtkWidget*>(target_window_->native_handle());
GdkDisplay* gdk_display = gtk_widget_get_display(window_handle);
assert(GDK_IS_X11_DISPLAY(gdk_display));
xcb_connection_t* connection =
XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display));
xcb_window_t window =
gdk_x11_window_get_xid(gtk_widget_get_window(window_handle));
VkXcbSurfaceCreateInfoKHR create_info;
create_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
create_info.pNext = nullptr;
create_info.flags = 0;
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);
CheckResult(status, "vkCreateXcbSurfaceKHR");
#else
#error Unsupported GDK Backend on Linux.
#endif // GDK_WINDOWING_X11
#else
#error Platform not yet implemented.
#endif // XE_PLATFORM_WIN32
if (status != VK_SUCCESS) {
XELOGE("Failed to create presentation surface");
return false;
}
swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(),
provider->device());
if (swap_chain_->Initialize(surface) != VK_SUCCESS) {
XELOGE("Unable to initialize swap chain");
return false;
}
// Only initialize immediate mode drawer if we are not an offscreen context.
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
status = immediate_drawer_->Initialize();
if (status != VK_SUCCESS) {
XELOGE("Failed to initialize the immediate mode drawer");
immediate_drawer_.reset();
return false;
}
}
return true;
}
bool VulkanContext::Initialize() { return false; }
ImmediateDrawer* VulkanContext::immediate_drawer() {
return immediate_drawer_.get();
}
VulkanInstance* VulkanContext::instance() const {
return static_cast<VulkanProvider*>(provider_)->instance();
}
void VulkanContext::BeginSwap() {}
VulkanDevice* VulkanContext::device() const {
return static_cast<VulkanProvider*>(provider_)->device();
}
bool VulkanContext::is_current() { return false; }
bool VulkanContext::MakeCurrent() {
SCOPE_profile_cpu_f("gpu");
return true;
}
void VulkanContext::ClearCurrent() {}
void VulkanContext::BeginSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
VkResult status;
// If we have a window see if it's been resized since we last swapped.
// If it has been, we'll need to reinitialize the swap chain before we
// start touching it.
if (target_window_) {
if (target_window_->scaled_width() != swap_chain_->surface_width() ||
target_window_->scaled_height() != swap_chain_->surface_height()) {
// Resized!
swap_chain_->Reinitialize();
}
}
if (!context_lost_) {
// Acquire the next image and set it up for use.
status = swap_chain_->Begin();
if (status == VK_ERROR_DEVICE_LOST) {
context_lost_ = true;
}
}
// 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());
}
void VulkanContext::EndSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
VkResult status;
if (!context_lost_) {
// Notify the presentation engine the image is ready.
// The contents must be in a coherent state.
status = swap_chain_->End();
if (status == VK_ERROR_DEVICE_LOST) {
context_lost_ = true;
}
}
// 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());
}
void VulkanContext::EndSwap() {}
std::unique_ptr<RawImage> VulkanContext::Capture() {
// TODO(benvanik): read back swap chain front buffer.
// TODO(Triang3l): Read back swap chain front buffer.
return nullptr;
}

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -13,51 +13,39 @@
#include <memory>
#include "xenia/ui/graphics_context.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
namespace xe {
namespace ui {
namespace vulkan {
class VulkanDevice;
class VulkanImmediateDrawer;
class VulkanInstance;
class VulkanProvider;
class VulkanSwapChain;
class VulkanContext : public GraphicsContext {
public:
~VulkanContext() override;
ImmediateDrawer* immediate_drawer() override;
VulkanSwapChain* swap_chain() const { return swap_chain_.get(); }
VulkanInstance* instance() const;
VulkanDevice* device() const;
bool is_current() override;
bool MakeCurrent() override;
void ClearCurrent() override;
bool WasLost() override { return context_lost_; }
// Returns true if the OS took away our context because we caused a TDR or
// some other outstanding error. When this happens, this context, as well as
// any other shared contexts are junk.
// This context must be made current in order for this call to work properly.
bool WasLost() override { return false; }
void BeginSwap() override;
void EndSwap() override;
std::unique_ptr<RawImage> Capture() override;
protected:
bool context_lost_ = false;
VulkanProvider* GetVulkanProvider() const {
return static_cast<VulkanProvider*>(provider_);
}
private:
friend class VulkanProvider;
explicit VulkanContext(VulkanProvider* provider, Window* target_window);
private:
bool Initialize();
std::unique_ptr<VulkanSwapChain> swap_chain_;
std::unique_ptr<VulkanImmediateDrawer> immediate_drawer_;
private:
std::unique_ptr<VulkanImmediateDrawer> immediate_drawer_ = nullptr;
};
} // namespace vulkan

View File

@@ -1,417 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2017 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_device.h"
#include <cinttypes>
#include <climits>
#include <mutex>
#include <string>
#include "third_party/renderdoc/renderdoc_app.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/profiling.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_instance.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
namespace vulkan {
VulkanDevice::VulkanDevice(VulkanInstance* instance) : instance_(instance) {
if (cvars::vulkan_validation) {
DeclareRequiredLayer("VK_LAYER_LUNARG_standard_validation",
Version::Make(0, 0, 0), true);
// DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects", Version::Make(0,
// 0, 0), true);
/*
DeclareRequiredLayer("VK_LAYER_GOOGLE_threading", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_core_validation",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_object_tracker",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_draw_state", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_parameter_validation",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_swapchain", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_device_limits",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_image", Version::Make(0, 0, 0), true);
*/
}
// AMD shader info (optional)
DeclareRequiredExtension(VK_AMD_SHADER_INFO_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
// Debug markers (optional)
DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
DeclareRequiredExtension(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
Version::Make(0, 0, 0), false);
}
VulkanDevice::~VulkanDevice() {
if (handle) {
vkDestroyDevice(handle, nullptr);
handle = nullptr;
}
}
bool VulkanDevice::Initialize(DeviceInfo device_info) {
// Gather list of enabled layer names.
auto layers_result = CheckRequirements(required_layers_, device_info.layers);
auto& enabled_layers = layers_result.second;
// Gather list of enabled extension names.
auto extensions_result =
CheckRequirements(required_extensions_, device_info.extensions);
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) {
FatalVulkanError(
"Layer and extension verification failed; aborting initialization");
return false;
}
// Query supported features so we can make sure we have what we need.
VkPhysicalDeviceFeatures supported_features;
vkGetPhysicalDeviceFeatures(device_info.handle, &supported_features);
VkPhysicalDeviceFeatures enabled_features = {0};
bool any_features_missing = false;
#define ENABLE_AND_EXPECT(name) \
if (!supported_features.name) { \
any_features_missing = true; \
FatalVulkanError("Vulkan device is missing feature " #name); \
} else { \
enabled_features.name = VK_TRUE; \
}
ENABLE_AND_EXPECT(shaderClipDistance);
ENABLE_AND_EXPECT(shaderCullDistance);
ENABLE_AND_EXPECT(shaderStorageImageExtendedFormats);
ENABLE_AND_EXPECT(shaderTessellationAndGeometryPointSize);
ENABLE_AND_EXPECT(samplerAnisotropy);
ENABLE_AND_EXPECT(geometryShader);
ENABLE_AND_EXPECT(depthClamp);
ENABLE_AND_EXPECT(multiViewport);
ENABLE_AND_EXPECT(independentBlend);
ENABLE_AND_EXPECT(textureCompressionBC);
// TODO(benvanik): add other features.
if (any_features_missing) {
XELOGE(
"One or more required device features are missing; aborting "
"initialization");
return false;
}
// Pick a queue.
// Any queue we use must support both graphics and presentation.
// TODO(benvanik): use multiple queues (DMA-only, compute-only, etc).
if (device_info.queue_family_properties.empty()) {
FatalVulkanError("No queue families available");
return false;
}
uint32_t ideal_queue_family_index = UINT_MAX;
uint32_t queue_count = 1;
for (size_t i = 0; i < device_info.queue_family_properties.size(); ++i) {
auto queue_flags = device_info.queue_family_properties[i].queueFlags;
if (queue_flags & VK_QUEUE_GRAPHICS_BIT &&
queue_flags & VK_QUEUE_TRANSFER_BIT) {
// Can do graphics and transfer - good!
ideal_queue_family_index = static_cast<uint32_t>(i);
// Grab all the queues we can.
queue_count = device_info.queue_family_properties[i].queueCount;
break;
}
}
if (ideal_queue_family_index == UINT_MAX) {
FatalVulkanError(
"No queue families available that can both do graphics and transfer");
return false;
}
// Some tools *cough* renderdoc *cough* can't handle multiple queues.
if (cvars::vulkan_primary_queue_only) {
queue_count = 1;
}
std::vector<VkDeviceQueueCreateInfo> queue_infos;
std::vector<std::vector<float>> queue_priorities;
queue_infos.resize(device_info.queue_family_properties.size());
queue_priorities.resize(queue_infos.size());
for (int i = 0; i < queue_infos.size(); i++) {
VkDeviceQueueCreateInfo& queue_info = queue_infos[i];
VkQueueFamilyProperties& family_props =
device_info.queue_family_properties[i];
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.pNext = nullptr;
queue_info.flags = 0;
queue_info.queueFamilyIndex = i;
queue_info.queueCount = family_props.queueCount;
queue_priorities[i].resize(family_props.queueCount, 0.f);
if (i == ideal_queue_family_index) {
// Prioritize the first queue on the primary queue family.
queue_priorities[i][0] = 1.0f;
}
queue_info.pQueuePriorities = queue_priorities[i].data();
}
VkDeviceCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size());
create_info.pQueueCreateInfos = queue_infos.data();
create_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
create_info.ppEnabledLayerNames = enabled_layers.data();
create_info.enabledExtensionCount =
static_cast<uint32_t>(enabled_extensions_.size());
create_info.ppEnabledExtensionNames = enabled_extensions_.data();
create_info.pEnabledFeatures = &enabled_features;
auto err = vkCreateDevice(device_info.handle, &create_info, nullptr, &handle);
switch (err) {
case VK_SUCCESS:
// Ok!
break;
case VK_ERROR_INITIALIZATION_FAILED:
FatalVulkanError("Device initialization failed; generic");
return false;
case VK_ERROR_EXTENSION_NOT_PRESENT:
FatalVulkanError(
"Device initialization failed; requested extension not present");
return false;
case VK_ERROR_LAYER_NOT_PRESENT:
FatalVulkanError(
"Device initialization failed; requested layer not present");
return false;
default:
FatalVulkanError(std::string("Device initialization failed; unknown: ") +
to_string(err));
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");
}
}
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_);
if (!primary_queue_) {
XELOGE("vkGetDeviceQueue returned nullptr!");
return false;
}
// Get all additional queues, if we got any.
free_queues_.resize(device_info_.queue_family_properties.size());
for (uint32_t i = 0; i < device_info_.queue_family_properties.size(); i++) {
VkQueueFamilyProperties& family_props =
device_info_.queue_family_properties[i];
for (uint32_t j = 0; j < family_props.queueCount; j++) {
VkQueue queue = nullptr;
if (i == queue_family_index_ && j == 0) {
// Already retrieved the primary queue index.
continue;
}
vkGetDeviceQueue(handle, i, j, &queue);
if (queue) {
free_queues_[i].push_back(queue);
}
}
}
XELOGVK("Device initialized successfully!");
return true;
}
bool VulkanDevice::HasEnabledExtension(const char* name) {
for (auto extension : enabled_extensions_) {
if (!std::strcmp(extension, name)) {
return true;
}
}
return false;
}
VkQueue VulkanDevice::AcquireQueue(uint32_t queue_family_index) {
std::lock_guard<std::mutex> lock(queue_mutex_);
if (free_queues_[queue_family_index].empty()) {
return nullptr;
}
auto queue = free_queues_[queue_family_index].back();
free_queues_[queue_family_index].pop_back();
return queue;
}
void VulkanDevice::ReleaseQueue(VkQueue queue, uint32_t queue_family_index) {
std::lock_guard<std::mutex> lock(queue_mutex_);
free_queues_[queue_family_index].push_back(queue);
}
void VulkanDevice::DbgSetObjectName(uint64_t object,
VkDebugReportObjectTypeEXT object_type,
std::string name) {
if (!debug_marker_ena_ || pfn_vkDebugMarkerSetObjectNameEXT_ == nullptr) {
// Extension disabled.
return;
}
VkDebugMarkerObjectNameInfoEXT info;
info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
info.pNext = nullptr;
info.objectType = object_type;
info.object = object;
info.pObjectName = name.c_str();
pfn_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) {
// Extension disabled.
return;
}
VkDebugMarkerMarkerInfoEXT info;
info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
info.pNext = nullptr;
info.pMarkerName = name.c_str();
info.color[0] = r;
info.color[1] = g;
info.color[2] = b;
info.color[3] = a;
pfn_vkCmdDebugMarkerBeginEXT_(command_buffer, &info);
}
void VulkanDevice::DbgMarkerEnd(VkCommandBuffer command_buffer) {
if (!debug_marker_ena_ || pfn_vkCmdDebugMarkerEndEXT_ == nullptr) {
// Extension disabled.
return;
}
pfn_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) {
// Extension disabled.
return;
}
VkDebugMarkerMarkerInfoEXT info;
info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
info.pNext = nullptr;
info.pMarkerName = name.c_str();
info.color[0] = r;
info.color[1] = g;
info.color[2] = g;
info.color[3] = b;
pfn_vkCmdDebugMarkerInsertEXT_(command_buffer, &info);
}
bool VulkanDevice::is_renderdoc_attached() const {
return instance_->is_renderdoc_attached();
}
void VulkanDevice::BeginRenderDocFrameCapture() {
auto api = reinterpret_cast<RENDERDOC_API_1_0_1*>(instance_->renderdoc_api());
if (!api) {
return;
}
assert_true(api->IsFrameCapturing() == 0);
api->StartFrameCapture(nullptr, nullptr);
}
void VulkanDevice::EndRenderDocFrameCapture() {
auto api = reinterpret_cast<RENDERDOC_API_1_0_1*>(instance_->renderdoc_api());
if (!api) {
return;
}
assert_true(api->IsFrameCapturing() == 1);
api->EndFrameCapture(nullptr, nullptr);
}
VkDeviceMemory VulkanDevice::AllocateMemory(
const VkMemoryRequirements& requirements, VkFlags required_properties) {
// Search memory types to find one matching our requirements and our
// properties.
uint32_t type_index = UINT_MAX;
for (uint32_t i = 0; i < device_info_.memory_properties.memoryTypeCount;
++i) {
const auto& memory_type = device_info_.memory_properties.memoryTypes[i];
if (((requirements.memoryTypeBits >> i) & 1) == 1) {
// Type is available for use; check for a match on properties.
if ((memory_type.propertyFlags & required_properties) ==
required_properties) {
type_index = i;
break;
}
}
}
if (type_index == UINT_MAX) {
XELOGE("Unable to find a matching memory type");
return nullptr;
}
// Allocate the memory.
VkMemoryAllocateInfo memory_info;
memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_info.pNext = nullptr;
memory_info.allocationSize = requirements.size;
memory_info.memoryTypeIndex = type_index;
VkDeviceMemory memory = nullptr;
auto err = vkAllocateMemory(handle, &memory_info, nullptr, &memory);
CheckResult(err, "vkAllocateMemory");
return memory;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,131 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_VULKAN_DEVICE_H_
#define XENIA_UI_VULKAN_VULKAN_DEVICE_H_
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
class VulkanInstance;
// Wrapper and utilities for VkDevice.
// Prefer passing this around over a VkDevice and casting as needed to call
// APIs.
class VulkanDevice {
public:
VulkanDevice(VulkanInstance* instance);
~VulkanDevice();
VkDevice handle = nullptr;
operator VkDevice() const { return handle; }
operator VkPhysicalDevice() const { return device_info_.handle; }
// Declares a layer to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredLayer(std::string name, uint32_t min_version,
bool is_optional) {
required_layers_.push_back({name, min_version, is_optional});
}
// Declares an extension to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredExtension(std::string name, uint32_t min_version,
bool is_optional) {
required_extensions_.push_back({name, min_version, is_optional});
}
// Initializes the device, querying and enabling extensions and layers and
// preparing the device for general use.
// If initialization succeeds it's likely that no more failures beyond runtime
// issues will occur.
bool Initialize(DeviceInfo device_info);
bool HasEnabledExtension(const char* name);
uint32_t queue_family_index() const { return queue_family_index_; }
std::mutex& primary_queue_mutex() { return queue_mutex_; }
// Access to the primary queue must be synchronized with primary_queue_mutex.
VkQueue primary_queue() const { return primary_queue_; }
const DeviceInfo& device_info() const { return device_info_; }
// Acquires a queue for exclusive use by the caller.
// The queue will not be touched by any other code until it's returned with
// ReleaseQueue.
// Not all devices support queues or only support a limited number. If this
// returns null the primary_queue should be used with the
// primary_queue_mutex.
// This method is thread safe.
VkQueue AcquireQueue(uint32_t queue_family_index);
// Releases a queue back to the device pool.
// This method is thread safe.
void ReleaseQueue(VkQueue queue, uint32_t queue_family_index);
void DbgSetObjectName(uint64_t object, VkDebugReportObjectTypeEXT object_type,
std::string name);
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);
void DbgMarkerInsert(VkCommandBuffer command_buffer, std::string name,
float r = 0.0f, float g = 0.0f, float b = 0.0f,
float a = 0.0f);
// True if RenderDoc is attached and available for use.
bool is_renderdoc_attached() const;
// Begins capturing the current frame in RenderDoc, if it is attached.
// Must be paired with EndRenderDocCapture. Multiple frames cannot be
// captured at the same time.
void BeginRenderDocFrameCapture();
// Ends a capture.
void EndRenderDocFrameCapture();
// Allocates memory of the given size matching the required properties.
VkDeviceMemory AllocateMemory(
const VkMemoryRequirements& requirements,
VkFlags required_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
private:
VulkanInstance* instance_ = nullptr;
std::vector<Requirement> required_layers_;
std::vector<Requirement> required_extensions_;
std::vector<const char*> enabled_extensions_;
bool debug_marker_ena_ = false;
PFN_vkDebugMarkerSetObjectNameEXT pfn_vkDebugMarkerSetObjectNameEXT_ =
nullptr;
PFN_vkCmdDebugMarkerBeginEXT pfn_vkCmdDebugMarkerBeginEXT_ = nullptr;
PFN_vkCmdDebugMarkerEndEXT pfn_vkCmdDebugMarkerEndEXT_ = nullptr;
PFN_vkCmdDebugMarkerInsertEXT pfn_vkCmdDebugMarkerInsertEXT_ = nullptr;
DeviceInfo device_info_;
uint32_t queue_family_index_ = 0;
std::mutex queue_mutex_;
VkQueue primary_queue_ = nullptr;
std::vector<std::vector<VkQueue>> free_queues_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_DEVICE_H_

View File

@@ -2,904 +2,36 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/graphics_context.h"
#include "xenia/ui/vulkan/vulkan_context.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_swap_chain.h"
namespace xe {
namespace ui {
namespace vulkan {
// Generated with `xenia-build genspirv`.
#include "xenia/ui/vulkan/shaders/bin/immediate_frag.h"
#include "xenia/ui/vulkan/shaders/bin/immediate_vert.h"
constexpr uint32_t kCircularBufferCapacity = 2 * 1024 * 1024;
class LightweightCircularBuffer {
public:
LightweightCircularBuffer(VulkanDevice* device) : device_(*device) {
buffer_capacity_ = xe::round_up(kCircularBufferCapacity, 4096);
// Index buffer.
VkBufferCreateInfo index_buffer_info;
index_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
index_buffer_info.pNext = nullptr;
index_buffer_info.flags = 0;
index_buffer_info.size = buffer_capacity_;
index_buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
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_);
CheckResult(status, "vkCreateBuffer");
// Vertex buffer.
VkBufferCreateInfo vertex_buffer_info;
vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
vertex_buffer_info.pNext = nullptr;
vertex_buffer_info.flags = 0;
vertex_buffer_info.size = buffer_capacity_;
vertex_buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
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_);
CheckResult(status, "vkCreateBuffer");
// Allocate underlying buffer.
// We alias it for both vertices and indices.
VkMemoryRequirements buffer_requirements;
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);
// Persistent mapping.
status = vkMapMemory(device_, buffer_memory_, 0, VK_WHOLE_SIZE, 0,
&buffer_data_);
CheckResult(status, "vkMapMemory");
}
~LightweightCircularBuffer() {
if (buffer_memory_) {
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);
}
VkBuffer vertex_buffer() const { return vertex_buffer_; }
VkBuffer index_buffer() const { return index_buffer_; }
// Allocates space for data and copies it into the buffer.
// Returns the offset in the buffer of the data or VK_WHOLE_SIZE if the buffer
// is full.
VkDeviceSize Emplace(const void* source_data, size_t source_length) {
// TODO(benvanik): query actual alignment.
source_length = xe::round_up(source_length, 256);
// Run down old fences to free up space.
// Check to see if we have space.
// return VK_WHOLE_SIZE;
// Compute new range and mark as in use.
if (current_offset_ + source_length > buffer_capacity_) {
// Wraps around.
current_offset_ = 0;
}
VkDeviceSize offset = current_offset_;
current_offset_ += source_length;
// Copy data.
auto dest_ptr = reinterpret_cast<uint8_t*>(buffer_data_) + offset;
std::memcpy(dest_ptr, source_data, source_length);
// Insert fence.
// TODO(benvanik): coarse-grained fences, these may be too fine.
// Flush memory.
// TODO(benvanik): do only in large batches? can barrier it.
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);
return offset;
}
private:
VkDevice device_ = nullptr;
VkBuffer index_buffer_ = nullptr;
VkBuffer vertex_buffer_ = nullptr;
VkDeviceMemory buffer_memory_ = nullptr;
void* buffer_data_ = nullptr;
size_t buffer_capacity_ = 0;
size_t current_offset_ = 0;
};
class VulkanImmediateTexture : public ImmediateTexture {
public:
VulkanImmediateTexture(VulkanDevice* device, VkDescriptorPool descriptor_pool,
VkSampler sampler, uint32_t width, uint32_t height)
: ImmediateTexture(width, height),
device_(device),
descriptor_pool_(descriptor_pool),
sampler_(sampler) {}
~VulkanImmediateTexture() override { Shutdown(); }
VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout,
VkImageView image_view) {
handle = reinterpret_cast<uintptr_t>(this);
image_view_ = image_view;
VkResult status;
// Create descriptor set used just for this texture.
// It never changes, so we can reuse it and not worry with updates.
VkDescriptorSetAllocateInfo set_alloc_info;
set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
set_alloc_info.pNext = nullptr;
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_);
CheckResult(status, "vkAllocateDescriptorSets");
if (status != VK_SUCCESS) {
return status;
}
// Initialize descriptor with our texture.
VkDescriptorImageInfo texture_info;
texture_info.sampler = sampler_;
texture_info.imageView = image_view_;
texture_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
VkWriteDescriptorSet descriptor_write;
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.pNext = nullptr;
descriptor_write.dstSet = descriptor_set_;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
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);
return VK_SUCCESS;
}
VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout) {
handle = reinterpret_cast<uintptr_t>(this);
VkResult status;
// Create image object.
VkImageCreateInfo image_info;
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = nullptr;
image_info.flags = 0;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.extent = {width, height, 1};
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = VK_IMAGE_TILING_LINEAR;
image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.queueFamilyIndexCount = 0;
image_info.pQueueFamilyIndices = nullptr;
image_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
status = vkCreateImage(*device_, &image_info, nullptr, &image_);
CheckResult(status, "vkCreateImage");
if (status != VK_SUCCESS) {
return status;
}
// Allocate memory for the image.
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(*device_, image_, &memory_requirements);
device_memory_ = device_->AllocateMemory(
memory_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (!device_memory_) {
return VK_ERROR_INITIALIZATION_FAILED;
}
// Bind memory and the image together.
status = vkBindImageMemory(*device_, image_, device_memory_, 0);
CheckResult(status, "vkBindImageMemory");
if (status != VK_SUCCESS) {
return status;
}
// Create image view used by the shader.
VkImageViewCreateInfo view_info;
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.pNext = nullptr;
view_info.flags = 0;
view_info.image = image_;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = VK_FORMAT_R8G8B8A8_UNORM;
view_info.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A,
};
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
status = vkCreateImageView(*device_, &view_info, nullptr, &image_view_);
CheckResult(status, "vkCreateImageView");
if (status != VK_SUCCESS) {
return status;
}
// Create descriptor set used just for this texture.
// It never changes, so we can reuse it and not worry with updates.
VkDescriptorSetAllocateInfo set_alloc_info;
set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
set_alloc_info.pNext = nullptr;
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_);
CheckResult(status, "vkAllocateDescriptorSets");
if (status != VK_SUCCESS) {
return status;
}
// Initialize descriptor with our texture.
VkDescriptorImageInfo texture_info;
texture_info.sampler = sampler_;
texture_info.imageView = image_view_;
texture_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
VkWriteDescriptorSet descriptor_write;
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.pNext = nullptr;
descriptor_write.dstSet = descriptor_set_;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
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);
return VK_SUCCESS;
}
void Shutdown() {
if (descriptor_set_) {
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);
}
VkResult Upload(const uint8_t* src_data) {
// TODO(benvanik): assert not in use? textures aren't dynamic right now.
// 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);
// Map memory for upload.
uint8_t* gpu_data = nullptr;
auto status = 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_);
}
return status;
}
// Queues a command to transition this texture to a new layout. This assumes
// the command buffer WILL be queued and executed by the device.
void TransitionLayout(VkCommandBuffer command_buffer,
VkImageLayout new_layout) {
VkImageMemoryBarrier image_barrier;
image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_barrier.pNext = nullptr;
image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier.srcAccessMask = 0;
image_barrier.dstAccessMask = 0;
image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_barrier.newLayout = new_layout;
image_barrier.image = image_;
image_barrier.subresourceRange = {0, 0, 1, 0, 1};
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);
}
VkDescriptorSet descriptor_set() const { return descriptor_set_; }
VkImageLayout layout() const { return image_layout_; }
private:
ui::vulkan::VulkanDevice* device_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkSampler sampler_ = nullptr; // Not owned.
VkImage image_ = nullptr;
VkImageLayout image_layout_ = VK_IMAGE_LAYOUT_PREINITIALIZED;
VkDeviceMemory device_memory_ = nullptr;
VkImageView image_view_ = nullptr;
VkDescriptorSet descriptor_set_ = nullptr;
};
VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
: ImmediateDrawer(graphics_context), context_(graphics_context) {}
VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); }
VkResult VulkanImmediateDrawer::Initialize() {
auto device = context_->device();
// NEAREST + CLAMP
VkSamplerCreateInfo sampler_info;
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.pNext = nullptr;
sampler_info.flags = 0;
sampler_info.magFilter = VK_FILTER_NEAREST;
sampler_info.minFilter = VK_FILTER_NEAREST;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
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;
sampler_info.mipLodBias = 0.0f;
sampler_info.anisotropyEnable = VK_FALSE;
sampler_info.maxAnisotropy = 1.0f;
sampler_info.compareEnable = VK_FALSE;
sampler_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_info.minLod = 0.0f;
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);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
// NEAREST + REPEAT
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);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
// LINEAR + CLAMP
sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR;
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);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
// LINEAR + REPEAT
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);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
// Create the descriptor set layout used for our texture sampler.
// As it changes almost every draw we keep it separate from the uniform buffer
// and cache it on the textures.
VkDescriptorSetLayoutCreateInfo texture_set_layout_info;
texture_set_layout_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
texture_set_layout_info.pNext = nullptr;
texture_set_layout_info.flags = 0;
texture_set_layout_info.bindingCount = 1;
VkDescriptorSetLayoutBinding texture_binding;
texture_binding.binding = 0;
texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
texture_binding.descriptorCount = 1;
texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_binding.pImmutableSamplers = nullptr;
texture_set_layout_info.pBindings = &texture_binding;
status = vkCreateDescriptorSetLayout(*device, &texture_set_layout_info,
nullptr, &texture_set_layout_);
CheckResult(status, "vkCreateDescriptorSetLayout");
if (status != VK_SUCCESS) {
return status;
}
// Descriptor pool used for all of our cached descriptors.
// In the steady state we don't allocate anything, so these are all manually
// managed.
VkDescriptorPoolCreateInfo descriptor_pool_info;
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptor_pool_info.pNext = nullptr;
descriptor_pool_info.flags =
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
descriptor_pool_info.maxSets = 128;
VkDescriptorPoolSize pool_sizes[1];
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
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_);
CheckResult(status, "vkCreateDescriptorPool");
if (status != VK_SUCCESS) {
return status;
}
// Create the pipeline layout used for our pipeline.
// If we had multiple pipelines they would share this.
VkPipelineLayoutCreateInfo pipeline_layout_info;
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.pNext = nullptr;
pipeline_layout_info.flags = 0;
VkDescriptorSetLayout set_layouts[] = {texture_set_layout_};
pipeline_layout_info.setLayoutCount =
static_cast<uint32_t>(xe::countof(set_layouts));
pipeline_layout_info.pSetLayouts = set_layouts;
VkPushConstantRange push_constant_ranges[2];
push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_ranges[0].offset = 0;
push_constant_ranges[0].size = sizeof(float) * 16;
push_constant_ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
push_constant_ranges[1].offset = sizeof(float) * 16;
push_constant_ranges[1].size = sizeof(int);
pipeline_layout_info.pushConstantRangeCount =
static_cast<uint32_t>(xe::countof(push_constant_ranges));
pipeline_layout_info.pPushConstantRanges = push_constant_ranges;
status = vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr,
&pipeline_layout_);
CheckResult(status, "vkCreatePipelineLayout");
if (status != VK_SUCCESS) {
return status;
}
// Vertex and fragment shaders.
VkShaderModuleCreateInfo vertex_shader_info;
vertex_shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vertex_shader_info.pNext = nullptr;
vertex_shader_info.flags = 0;
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);
CheckResult(status, "vkCreateShaderModule");
VkShaderModuleCreateInfo fragment_shader_info;
fragment_shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
fragment_shader_info.pNext = nullptr;
fragment_shader_info.flags = 0;
fragment_shader_info.codeSize = sizeof(immediate_frag);
fragment_shader_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_frag);
VkShaderModule fragment_shader;
status = vkCreateShaderModule(*device, &fragment_shader_info, nullptr,
&fragment_shader);
CheckResult(status, "vkCreateShaderModule");
// Pipeline used when rendering triangles.
VkGraphicsPipelineCreateInfo pipeline_info;
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.pNext = nullptr;
pipeline_info.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
VkPipelineShaderStageCreateInfo pipeline_stages[2];
pipeline_stages[0].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline_stages[0].pNext = nullptr;
pipeline_stages[0].flags = 0;
pipeline_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
pipeline_stages[0].module = vertex_shader;
pipeline_stages[0].pName = "main";
pipeline_stages[0].pSpecializationInfo = nullptr;
pipeline_stages[1].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline_stages[1].pNext = nullptr;
pipeline_stages[1].flags = 0;
pipeline_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
pipeline_stages[1].module = fragment_shader;
pipeline_stages[1].pName = "main";
pipeline_stages[1].pSpecializationInfo = nullptr;
pipeline_info.stageCount = 2;
pipeline_info.pStages = pipeline_stages;
VkPipelineVertexInputStateCreateInfo vertex_state_info;
vertex_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_state_info.pNext = nullptr;
vertex_state_info.flags = 0;
VkVertexInputBindingDescription vertex_binding_descrs[1];
vertex_binding_descrs[0].binding = 0;
vertex_binding_descrs[0].stride = sizeof(ImmediateVertex);
vertex_binding_descrs[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
vertex_state_info.vertexBindingDescriptionCount =
static_cast<uint32_t>(xe::countof(vertex_binding_descrs));
vertex_state_info.pVertexBindingDescriptions = vertex_binding_descrs;
VkVertexInputAttributeDescription vertex_attrib_descrs[3];
vertex_attrib_descrs[0].location = 0;
vertex_attrib_descrs[0].binding = 0;
vertex_attrib_descrs[0].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attrib_descrs[0].offset = offsetof(ImmediateVertex, x);
vertex_attrib_descrs[1].location = 1;
vertex_attrib_descrs[1].binding = 0;
vertex_attrib_descrs[1].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attrib_descrs[1].offset = offsetof(ImmediateVertex, u);
vertex_attrib_descrs[2].location = 2;
vertex_attrib_descrs[2].binding = 0;
vertex_attrib_descrs[2].format = VK_FORMAT_R8G8B8A8_UNORM;
vertex_attrib_descrs[2].offset = offsetof(ImmediateVertex, color);
vertex_state_info.vertexAttributeDescriptionCount =
static_cast<uint32_t>(xe::countof(vertex_attrib_descrs));
vertex_state_info.pVertexAttributeDescriptions = vertex_attrib_descrs;
pipeline_info.pVertexInputState = &vertex_state_info;
VkPipelineInputAssemblyStateCreateInfo input_info;
input_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_info.pNext = nullptr;
input_info.flags = 0;
input_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_info.primitiveRestartEnable = VK_FALSE;
pipeline_info.pInputAssemblyState = &input_info;
pipeline_info.pTessellationState = nullptr;
VkPipelineViewportStateCreateInfo viewport_state_info;
viewport_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_info.pNext = nullptr;
viewport_state_info.flags = 0;
viewport_state_info.viewportCount = 1;
viewport_state_info.pViewports = nullptr;
viewport_state_info.scissorCount = 1;
viewport_state_info.pScissors = nullptr;
pipeline_info.pViewportState = &viewport_state_info;
VkPipelineRasterizationStateCreateInfo rasterization_info;
rasterization_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_info.pNext = nullptr;
rasterization_info.flags = 0;
rasterization_info.depthClampEnable = VK_FALSE;
rasterization_info.rasterizerDiscardEnable = VK_FALSE;
rasterization_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_info.cullMode = VK_CULL_MODE_NONE;
rasterization_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization_info.depthBiasEnable = VK_FALSE;
rasterization_info.depthBiasConstantFactor = 0;
rasterization_info.depthBiasClamp = 0;
rasterization_info.depthBiasSlopeFactor = 0;
rasterization_info.lineWidth = 1.0f;
pipeline_info.pRasterizationState = &rasterization_info;
VkPipelineMultisampleStateCreateInfo multisample_info;
multisample_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_info.pNext = nullptr;
multisample_info.flags = 0;
multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_info.sampleShadingEnable = VK_FALSE;
multisample_info.minSampleShading = 0;
multisample_info.pSampleMask = nullptr;
multisample_info.alphaToCoverageEnable = VK_FALSE;
multisample_info.alphaToOneEnable = VK_FALSE;
pipeline_info.pMultisampleState = &multisample_info;
pipeline_info.pDepthStencilState = nullptr;
VkPipelineColorBlendStateCreateInfo blend_info;
blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_info.pNext = nullptr;
blend_info.flags = 0;
blend_info.logicOpEnable = VK_FALSE;
blend_info.logicOp = VK_LOGIC_OP_NO_OP;
VkPipelineColorBlendAttachmentState blend_attachments[1];
blend_attachments[0].blendEnable = VK_TRUE;
blend_attachments[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachments[0].dstColorBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachments[0].colorBlendOp = VK_BLEND_OP_ADD;
blend_attachments[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachments[0].dstAlphaBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachments[0].alphaBlendOp = VK_BLEND_OP_ADD;
blend_attachments[0].colorWriteMask = 0xF;
blend_info.attachmentCount =
static_cast<uint32_t>(xe::countof(blend_attachments));
blend_info.pAttachments = blend_attachments;
std::memset(blend_info.blendConstants, 0, sizeof(blend_info.blendConstants));
pipeline_info.pColorBlendState = &blend_info;
VkPipelineDynamicStateCreateInfo dynamic_state_info;
dynamic_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_info.pNext = nullptr;
dynamic_state_info.flags = 0;
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
dynamic_state_info.dynamicStateCount =
static_cast<uint32_t>(xe::countof(dynamic_states));
dynamic_state_info.pDynamicStates = dynamic_states;
pipeline_info.pDynamicState = &dynamic_state_info;
pipeline_info.layout = pipeline_layout_;
pipeline_info.renderPass = context_->swap_chain()->render_pass();
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = nullptr;
pipeline_info.basePipelineIndex = -1;
if (status == VK_SUCCESS) {
status = vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info,
nullptr, &triangle_pipeline_);
CheckResult(status, "vkCreateGraphicsPipelines");
}
// Silly, but let's make a pipeline just for drawing lines.
pipeline_info.flags = VK_PIPELINE_CREATE_DERIVATIVE_BIT;
input_info.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
pipeline_info.basePipelineHandle = triangle_pipeline_;
pipeline_info.basePipelineIndex = -1;
if (status == VK_SUCCESS) {
status = 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);
// Allocate the buffer we'll use for our vertex and index data.
circular_buffer_ = std::make_unique<LightweightCircularBuffer>(device);
return status;
}
void VulkanImmediateDrawer::Shutdown() {
auto device = context_->device();
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);
VK_SAFE_DESTROY(vkDestroyDescriptorPool, *device, descriptor_pool_, nullptr);
VK_SAFE_DESTROY(vkDestroyDescriptorSetLayout, *device, texture_set_layout_,
nullptr);
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);
}
std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::CreateTexture(
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
const uint8_t* data) {
auto device = context_->device();
VkResult status;
VkSampler sampler = GetSampler(filter, repeat);
auto texture = std::make_unique<VulkanImmediateTexture>(
device, descriptor_pool_, sampler, width, height);
status = texture->Initialize(texture_set_layout_);
if (status != VK_SUCCESS) {
texture->Shutdown();
return nullptr;
}
if (data) {
UpdateTexture(texture.get(), data);
}
return std::unique_ptr<ImmediateTexture>(texture.release());
}
std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::WrapTexture(
VkImageView image_view, VkSampler sampler, uint32_t width,
uint32_t height) {
VkResult status;
auto texture = std::make_unique<VulkanImmediateTexture>(
context_->device(), descriptor_pool_, sampler, width, height);
status = texture->Initialize(texture_set_layout_, image_view);
if (status != VK_SUCCESS) {
texture->Shutdown();
return nullptr;
}
return texture;
return nullptr;
}
void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
const uint8_t* data) {
static_cast<VulkanImmediateTexture*>(texture)->Upload(data);
}
const uint8_t* data) {}
void VulkanImmediateDrawer::Begin(int render_target_width,
int render_target_height) {
auto device = context_->device();
auto swap_chain = context_->swap_chain();
assert_null(current_cmd_buffer_);
current_cmd_buffer_ = swap_chain->render_cmd_buffer();
current_render_target_width_ = render_target_width;
current_render_target_height_ = render_target_height;
int render_target_height) {}
// Viewport changes only once per batch.
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(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);
void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {}
// Update projection matrix.
const float ortho_projection[4][4] = {
{2.0f / render_target_width, 0.0f, 0.0f, 0.0f},
{0.0f, 2.0f / -render_target_height, 0.0f, 0.0f},
{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);
}
void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
auto device = context_->device();
// Upload vertices.
VkDeviceSize vertices_offset = circular_buffer_->Emplace(
batch.vertices, batch.vertex_count * sizeof(ImmediateVertex));
if (vertices_offset == VK_WHOLE_SIZE) {
// TODO(benvanik): die?
return;
}
auto vertex_buffer = circular_buffer_->vertex_buffer();
vkCmdBindVertexBuffers(current_cmd_buffer_, 0, 1, &vertex_buffer,
&vertices_offset);
// Upload indices.
if (batch.indices) {
VkDeviceSize indices_offset = circular_buffer_->Emplace(
batch.indices, batch.index_count * sizeof(uint16_t));
if (indices_offset == VK_WHOLE_SIZE) {
// TODO(benvanik): die?
return;
}
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) {
switch (draw.primitive_type) {
case ImmediatePrimitiveType::kLines:
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_);
break;
}
// Setup texture binding.
auto texture = reinterpret_cast<VulkanImmediateTexture*>(draw.texture_handle);
if (texture) {
if (texture->layout() != VK_IMAGE_LAYOUT_GENERAL) {
texture->TransitionLayout(current_cmd_buffer_, VK_IMAGE_LAYOUT_GENERAL);
}
auto texture_set = texture->descriptor_set();
if (!texture_set) {
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);
}
// Use push constants for our per-draw changes.
// Here, the restrict_texture_samples uniform.
int restrict_texture_samples = draw.restrict_texture_samples ? 1 : 0;
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.
VkRect2D scissor;
if (draw.scissor) {
scissor.offset.x = draw.scissor_rect[0];
scissor.offset.y = current_render_target_height_ -
(draw.scissor_rect[1] + draw.scissor_rect[3]);
scissor.extent.width = draw.scissor_rect[2];
scissor.extent.height = draw.scissor_rect[3];
} else {
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = current_render_target_width_;
scissor.extent.height = current_render_target_height_;
}
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);
} else {
vkCmdDraw(current_cmd_buffer_, draw.count, 1, draw.base_vertex, 0);
}
}
void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {}
void VulkanImmediateDrawer::EndDrawBatch() {}
void VulkanImmediateDrawer::End() { current_cmd_buffer_ = nullptr; }
VkSampler VulkanImmediateDrawer::GetSampler(ImmediateTextureFilter filter,
bool repeat) {
VkSampler sampler = nullptr;
switch (filter) {
case ImmediateTextureFilter::kNearest:
sampler = repeat ? samplers_.nearest_repeat : samplers_.nearest_clamp;
break;
case ImmediateTextureFilter::kLinear:
sampler = repeat ? samplers_.linear_repeat : samplers_.linear_clamp;
break;
default:
assert_unhandled_case(filter);
sampler = samplers_.nearest_clamp;
break;
}
return sampler;
}
void VulkanImmediateDrawer::End() {}
} // namespace vulkan
} // namespace ui

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -10,35 +10,19 @@
#ifndef XENIA_UI_VULKAN_VULKAN_IMMEDIATE_DRAWER_H_
#define XENIA_UI_VULKAN_VULKAN_IMMEDIATE_DRAWER_H_
#include <memory>
#include "xenia/ui/immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan.h"
namespace xe {
namespace ui {
namespace vulkan {
class LightweightCircularBuffer;
class VulkanContext;
class VulkanImmediateDrawer : public ImmediateDrawer {
public:
VulkanImmediateDrawer(VulkanContext* graphics_context);
~VulkanImmediateDrawer() override;
VkResult Initialize();
void Shutdown();
std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width,
uint32_t height,
ImmediateTextureFilter filter,
bool repeat,
const uint8_t* data) override;
std::unique_ptr<ImmediateTexture> WrapTexture(VkImageView image_view,
VkSampler sampler,
uint32_t width,
uint32_t height);
void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override;
void Begin(int render_target_width, int render_target_height) override;
@@ -46,31 +30,6 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
void Draw(const ImmediateDraw& draw) override;
void EndDrawBatch() override;
void End() override;
VkSampler GetSampler(ImmediateTextureFilter filter, bool repeat);
private:
VulkanContext* context_ = nullptr;
struct {
VkSampler nearest_clamp = nullptr;
VkSampler nearest_repeat = nullptr;
VkSampler linear_clamp = nullptr;
VkSampler linear_repeat = nullptr;
} samplers_;
VkDescriptorSetLayout texture_set_layout_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkPipelineLayout pipeline_layout_ = nullptr;
VkPipeline triangle_pipeline_ = nullptr;
VkPipeline line_pipeline_ = nullptr;
std::unique_ptr<LightweightCircularBuffer> circular_buffer_;
bool batch_has_index_buffer_ = false;
VkCommandBuffer current_cmd_buffer_ = nullptr;
int current_render_target_width_ = 0;
int current_render_target_height_ = 0;
};
} // namespace vulkan

View File

@@ -1,540 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_instance.h"
#include <cinttypes>
#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/profiling.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
#if XE_PLATFORM_LINUX
#include "xenia/ui/window_gtk.h"
#endif
#define VK_API_VERSION VK_API_VERSION_1_1
namespace xe {
namespace ui {
namespace vulkan {
VulkanInstance::VulkanInstance() {
if (cvars::vulkan_validation) {
DeclareRequiredLayer("VK_LAYER_LUNARG_standard_validation",
Version::Make(0, 0, 0), true);
// DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects", Version::Make(0,
// 0, 0), true);
/*
DeclareRequiredLayer("VK_LAYER_GOOGLE_threading", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_core_validation",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_object_tracker",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_draw_state", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_parameter_validation",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_swapchain", Version::Make(0, 0, 0),
true);
DeclareRequiredLayer("VK_LAYER_LUNARG_device_limits",
Version::Make(0, 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_image", Version::Make(0, 0, 0), true);
*/
DeclareRequiredExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
}
DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
Version::Make(0, 0, 0), true);
}
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!");
return false;
}
// Get all of the global layers and extensions provided by the system.
if (!QueryGlobals()) {
XELOGE("Failed to query instance globals");
return false;
}
// Create the vulkan instance used by the application with our required
// extensions and layers.
if (!CreateInstance()) {
XELOGE("Failed to create instance");
return false;
}
// Query available devices so that we can pick one.
if (!QueryDevices()) {
XELOGE("Failed to query devices");
return false;
}
// Hook into renderdoc, if it's available.
EnableRenderDoc();
XELOGVK("Instance initialized successfully!");
return true;
}
bool VulkanInstance::EnableRenderDoc() {
// RenderDoc injects itself into our process, so we should be able to get it.
pRENDERDOC_GetAPI get_api = nullptr;
#if XE_PLATFORM_WIN32
auto module_handle = GetModuleHandleW(L"renderdoc.dll");
if (!module_handle) {
XELOGI("RenderDoc support requested but it is not attached");
return false;
}
get_api = reinterpret_cast<pRENDERDOC_GetAPI>(
GetProcAddress(module_handle, "RENDERDOC_GetAPI"));
#else
// TODO(benvanik): dlsym/etc - abstracted in base/.
#endif // XE_PLATFORM_32
if (!get_api) {
XELOGI("RenderDoc support requested but it is not attached");
return false;
}
// Request all API function pointers.
if (!get_api(eRENDERDOC_API_Version_1_0_1,
reinterpret_cast<void**>(&renderdoc_api_))) {
XELOGE("RenderDoc found but was unable to get API - version mismatch?");
return false;
}
auto api = reinterpret_cast<RENDERDOC_API_1_0_1*>(renderdoc_api_);
// Query version.
int major;
int minor;
int patch;
api->GetAPIVersion(&major, &minor, &patch);
XELOGI("RenderDoc attached; {}.{}.{}", major, minor, patch);
is_renderdoc_attached_ = true;
return true;
}
bool VulkanInstance::QueryGlobals() {
// Scan global layers and accumulate properties.
// We do this in a loop so that we can allocate the required amount of
// memory and handle race conditions while querying.
uint32_t count = 0;
std::vector<VkLayerProperties> global_layer_properties;
VkResult err;
do {
err = vkEnumerateInstanceLayerProperties(&count, nullptr);
CheckResult(err, "vkEnumerateInstanceLayerProperties");
global_layer_properties.resize(count);
err = vkEnumerateInstanceLayerProperties(&count,
global_layer_properties.data());
} while (err == VK_INCOMPLETE);
CheckResult(err, "vkEnumerateInstanceLayerProperties");
global_layers_.resize(count);
for (size_t i = 0; i < global_layers_.size(); ++i) {
auto& global_layer = global_layers_[i];
global_layer.properties = global_layer_properties[i];
// Get all extensions available for the layer.
do {
err = vkEnumerateInstanceExtensionProperties(
global_layer.properties.layerName, &count, nullptr);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
global_layer.extensions.resize(count);
err = vkEnumerateInstanceExtensionProperties(
global_layer.properties.layerName, &count,
global_layer.extensions.data());
} while (err == VK_INCOMPLETE);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
}
XELOGVK("Found {} global layers:", global_layers_.size());
for (size_t i = 0; i < global_layers_.size(); ++i) {
auto& global_layer = global_layers_[i];
auto spec_version = Version::Parse(global_layer.properties.specVersion);
auto impl_version =
Version::Parse(global_layer.properties.implementationVersion);
XELOGVK("- {} (spec: {}, impl: {})", global_layer.properties.layerName,
spec_version.pretty_string, impl_version.pretty_string);
XELOGVK(" {}", global_layer.properties.description);
if (!global_layer.extensions.empty()) {
XELOGVK(" {} extensions:", global_layer.extensions.size());
DumpExtensions(global_layer.extensions, " ");
}
}
// Scan global extensions.
do {
err = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
global_extensions_.resize(count);
err = vkEnumerateInstanceExtensionProperties(nullptr, &count,
global_extensions_.data());
} while (err == VK_INCOMPLETE);
CheckResult(err, "vkEnumerateInstanceExtensionProperties");
XELOGVK("Found {} global extensions:", global_extensions_.size());
DumpExtensions(global_extensions_, "");
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 = 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 entrypoints and extensions.
volkLoadInstance(handle);
// Enable debug validation, if needed.
EnableDebugValidation();
return true;
}
void VulkanInstance::DestroyInstance() {
if (!handle) {
return;
}
DisableDebugValidation();
vkDestroyInstance(handle, nullptr);
handle = nullptr;
}
VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage, void* pUserData) {
if (strcmp(pLayerPrefix, "Validation") == 0) {
const char* blacklist[] = {
"bound but it was never updated. You may want to either update it or "
"not bind it.",
"is being used in draw but has not been updated.",
};
for (uint32_t i = 0; i < xe::countof(blacklist); ++i) {
if (strstr(pMessage, blacklist[i]) != nullptr) {
return false;
}
}
}
auto instance = reinterpret_cast<VulkanInstance*>(pUserData);
const char* message_type = "UNKNOWN";
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
message_type = "ERROR";
} else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
message_type = "WARN";
} else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
message_type = "PERF WARN";
} else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
message_type = "INFO";
} else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
message_type = "DEBUG";
}
XELOGVK("[{}/{}:{}] {}", pLayerPrefix, message_type, messageCode, pMessage);
return false;
}
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) {
XELOGVK("Debug validation layer not installed; ignoring");
return;
}
VkDebugReportCallbackCreateInfoEXT create_info;
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
create_info.pNext = nullptr;
// TODO(benvanik): flags to set these.
create_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;
create_info.pfnCallback = &DebugMessageCallback;
create_info.pUserData = this;
auto status = vk_create_debug_report_callback_ext(
handle, &create_info, nullptr, &dbg_report_callback_);
if (status == VK_SUCCESS) {
XELOGVK("Debug validation layer enabled");
} else {
XELOGVK("Debug validation layer failed to install; error {}",
to_string(status));
}
}
void VulkanInstance::DisableDebugValidation() {
if (!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);
dbg_report_callback_ = nullptr;
}
bool VulkanInstance::QueryDevices() {
// Get handles to all devices.
uint32_t count = 0;
std::vector<VkPhysicalDevice> device_handles;
auto err = vkEnumeratePhysicalDevices(handle, &count, nullptr);
CheckResult(err, "vkEnumeratePhysicalDevices");
device_handles.resize(count);
err = vkEnumeratePhysicalDevices(handle, &count, device_handles.data());
CheckResult(err, "vkEnumeratePhysicalDevices");
// Query device info.
for (size_t i = 0; i < device_handles.size(); ++i) {
auto device_handle = device_handles[i];
DeviceInfo device_info;
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);
// Gather queue family properties.
vkGetPhysicalDeviceQueueFamilyProperties(device_handle, &count, nullptr);
device_info.queue_family_properties.resize(count);
vkGetPhysicalDeviceQueueFamilyProperties(
device_handle, &count, device_info.queue_family_properties.data());
// Gather layers.
std::vector<VkLayerProperties> layer_properties;
err = vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr);
CheckResult(err, "vkEnumerateDeviceLayerProperties");
layer_properties.resize(count);
err = 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(
device_handle, layer_info.properties.layerName, &count, nullptr);
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
layer_info.extensions.resize(count);
err = vkEnumerateDeviceExtensionProperties(
device_handle, layer_info.properties.layerName, &count,
layer_info.extensions.data());
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
device_info.layers.push_back(std::move(layer_info));
}
// Gather extensions.
err = vkEnumerateDeviceExtensionProperties(device_handle, nullptr, &count,
nullptr);
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
device_info.extensions.resize(count);
err = vkEnumerateDeviceExtensionProperties(device_handle, nullptr, &count,
device_info.extensions.data());
CheckResult(err, "vkEnumerateDeviceExtensionProperties");
available_devices_.push_back(std::move(device_info));
}
XELOGVK("Found {} physical devices:", available_devices_.size());
for (size_t i = 0; i < available_devices_.size(); ++i) {
auto& device_info = available_devices_[i];
XELOGVK("- Device {}:", i);
DumpDeviceInfo(device_info);
}
return true;
}
void VulkanInstance::DumpLayers(const std::vector<LayerInfo>& layers,
const char* indent) {
for (size_t i = 0; i < layers.size(); ++i) {
auto& layer = layers[i];
auto spec_version = Version::Parse(layer.properties.specVersion);
auto impl_version = Version::Parse(layer.properties.implementationVersion);
XELOGVK("{}- {} (spec: {}, impl: {})", indent, layer.properties.layerName,
spec_version.pretty_string, impl_version.pretty_string);
XELOGVK("{} {}", indent, layer.properties.description);
if (!layer.extensions.empty()) {
XELOGVK("{} {} extensions:", indent, layer.extensions.size());
DumpExtensions(layer.extensions, std::strlen(indent) ? " " : " ");
}
}
}
void VulkanInstance::DumpExtensions(
const std::vector<VkExtensionProperties>& extensions, const char* indent) {
for (size_t i = 0; i < extensions.size(); ++i) {
auto& extension = extensions[i];
auto version = Version::Parse(extension.specVersion);
XELOGVK("{}- {} ({})", indent, extension.extensionName,
version.pretty_string);
}
}
void VulkanInstance::DumpDeviceInfo(const DeviceInfo& device_info) {
auto& properties = device_info.properties;
auto api_version = Version::Parse(properties.apiVersion);
auto driver_version = Version::Parse(properties.driverVersion);
XELOGVK(" apiVersion = {}", api_version.pretty_string);
XELOGVK(" driverVersion = {}", driver_version.pretty_string);
XELOGVK(" vendorId = {:#04x}", properties.vendorID);
XELOGVK(" deviceId = {:#04x}", properties.deviceID);
XELOGVK(" deviceType = {}", to_string(properties.deviceType));
XELOGVK(" deviceName = {}", properties.deviceName);
auto& memory_props = device_info.memory_properties;
XELOGVK(" Memory Heaps:");
for (size_t j = 0; j < memory_props.memoryHeapCount; ++j) {
XELOGVK(" - Heap {}: {} bytes", j, memory_props.memoryHeaps[j].size);
for (size_t k = 0; k < memory_props.memoryTypeCount; ++k) {
if (memory_props.memoryTypes[k].heapIndex == j) {
XELOGVK(" - Type {}:", k);
auto type_flags = memory_props.memoryTypes[k].propertyFlags;
if (!type_flags) {
XELOGVK(" VK_MEMORY_PROPERTY_DEVICE_ONLY");
}
if (type_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
XELOGVK(" VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT");
}
if (type_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
XELOGVK(" VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT");
}
if (type_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
XELOGVK(" VK_MEMORY_PROPERTY_HOST_COHERENT_BIT");
}
if (type_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
XELOGVK(" VK_MEMORY_PROPERTY_HOST_CACHED_BIT");
}
if (type_flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
XELOGVK(" VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT");
}
}
}
}
XELOGVK(" Queue Families:");
for (size_t j = 0; j < device_info.queue_family_properties.size(); ++j) {
auto& queue_props = device_info.queue_family_properties[j];
XELOGVK(" - Queue {}:", j);
XELOGVK(
" queueFlags = {}{}{}{}",
(queue_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) ? "graphics, " : "",
(queue_props.queueFlags & VK_QUEUE_COMPUTE_BIT) ? "compute, " : "",
(queue_props.queueFlags & VK_QUEUE_TRANSFER_BIT) ? "transfer, " : "",
(queue_props.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) ? "sparse, "
: "");
XELOGVK(" queueCount = {}", queue_props.queueCount);
XELOGVK(" timestampValidBits = {}", queue_props.timestampValidBits);
}
XELOGVK(" Layers:");
DumpLayers(device_info.layers, " ");
XELOGVK(" Extensions:");
DumpExtensions(device_info.extensions, " ");
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,105 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_VULKAN_INSTANCE_H_
#define XENIA_UI_VULKAN_VULKAN_INSTANCE_H_
#include <memory>
#include <string>
#include <vector>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
namespace vulkan {
// Wrappers and utilities for VkInstance.
class VulkanInstance {
public:
VulkanInstance();
~VulkanInstance();
VkInstance handle = nullptr;
operator VkInstance() const { return handle; }
// Declares a layer to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredLayer(std::string name, uint32_t min_version,
bool is_optional) {
required_layers_.push_back({name, min_version, is_optional});
}
// Declares an extension to verify and enable upon initialization.
// Must be called before Initialize.
void DeclareRequiredExtension(std::string name, uint32_t min_version,
bool is_optional) {
required_extensions_.push_back({name, min_version, is_optional});
}
// Initializes the instance, querying and enabling extensions and layers and
// preparing the instance for general use.
// If initialization succeeds it's likely that no more failures beyond runtime
// issues will occur.
bool Initialize();
// Returns a list of all available devices as detected during initialization.
const std::vector<DeviceInfo>& available_devices() const {
return available_devices_;
}
// True if RenderDoc is attached and available for use.
bool is_renderdoc_attached() const { return is_renderdoc_attached_; }
// RenderDoc API handle, if attached.
void* renderdoc_api() const { return renderdoc_api_; }
private:
// Attempts to enable RenderDoc via the API, if it is attached.
bool EnableRenderDoc();
// Queries the system to find global extensions and layers.
bool QueryGlobals();
// Creates the instance, enabling required extensions and layers.
bool CreateInstance();
void DestroyInstance();
// Enables debugging info and callbacks for supported layers.
void EnableDebugValidation();
void DisableDebugValidation();
// Queries all available physical devices.
bool QueryDevices();
void DumpLayers(const std::vector<LayerInfo>& layers, const char* indent);
void DumpExtensions(const std::vector<VkExtensionProperties>& extensions,
const char* indent);
void DumpDeviceInfo(const DeviceInfo& device_info);
std::vector<Requirement> required_layers_;
std::vector<Requirement> required_extensions_;
std::vector<LayerInfo> global_layers_;
std::vector<VkExtensionProperties> global_extensions_;
std::vector<DeviceInfo> available_devices_;
VkDebugReportCallbackEXT dbg_report_callback_ = nullptr;
void* renderdoc_api_ = nullptr;
bool is_renderdoc_attached_ = false;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_INSTANCE_H_

View File

@@ -1,44 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#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"
namespace xe {
namespace ui {
namespace vulkan {
inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs) {
vma_funcs->vkGetPhysicalDeviceProperties = 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;
}
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_

View File

@@ -2,23 +2,15 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_provider.h"
#include <algorithm>
#include "xenia/base/logging.h"
#include "xenia/ui/vulkan/vulkan_context.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_instance.h"
#include "xenia/ui/vulkan/vulkan_util.h"
DEFINE_uint64(vulkan_device_index, 0, "Index of the physical device to use.",
"Vulkan");
namespace xe {
namespace ui {
@@ -28,10 +20,11 @@ std::unique_ptr<VulkanProvider> VulkanProvider::Create(Window* main_window) {
std::unique_ptr<VulkanProvider> provider(new VulkanProvider(main_window));
if (!provider->Initialize()) {
xe::FatalError(
"Unable to initialize Vulkan graphics subsystem.\n"
"Unable to initialize Direct3D 12 graphics subsystem.\n"
"\n"
"Ensure you have the latest drivers for your GPU and that it "
"supports Vulkan.\n"
"Ensure that you have the latest drivers for your GPU and it supports "
"Vulkan, and that you have the latest Vulkan runtime installed, which "
"can be downloaded at https://vulkan.lunarg.com/sdk/home.\n"
"\n"
"See https://xenia.jp/faq/ for more information and a list of "
"supported GPUs.");
@@ -43,49 +36,7 @@ std::unique_ptr<VulkanProvider> VulkanProvider::Create(Window* main_window) {
VulkanProvider::VulkanProvider(Window* main_window)
: GraphicsProvider(main_window) {}
VulkanProvider::~VulkanProvider() {
device_.reset();
instance_.reset();
}
bool VulkanProvider::Initialize() {
instance_ = std::make_unique<VulkanInstance>();
// Always enable the swapchain.
#if XE_PLATFORM_WIN32
instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0),
false);
instance_->DeclareRequiredExtension("VK_KHR_win32_surface",
Version::Make(0, 0, 0), false);
#endif
// Attempt initialization and device query.
if (!instance_->Initialize()) {
XELOGE("Failed to initialize vulkan instance");
return false;
}
// Pick the device to use.
auto available_devices = instance_->available_devices();
if (available_devices.empty()) {
XELOGE("No devices available for use");
return false;
}
size_t device_index =
std::min(available_devices.size(), cvars::vulkan_device_index);
auto& device_info = available_devices[device_index];
// Create the device.
device_ = std::make_unique<VulkanDevice>(instance_.get());
device_->DeclareRequiredExtension("VK_KHR_swapchain", Version::Make(0, 0, 0),
false);
if (!device_->Initialize(device_info)) {
XELOGE("Unable to initialize device");
return false;
}
return true;
}
bool VulkanProvider::Initialize() { return false; }
std::unique_ptr<GraphicsContext> VulkanProvider::CreateContext(
Window* target_window) {

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -18,29 +18,18 @@ namespace xe {
namespace ui {
namespace vulkan {
class VulkanDevice;
class VulkanInstance;
class VulkanProvider : public GraphicsProvider {
public:
~VulkanProvider() override;
static std::unique_ptr<VulkanProvider> Create(Window* main_window);
VulkanInstance* instance() const { return instance_.get(); }
VulkanDevice* device() const { return device_.get(); }
std::unique_ptr<GraphicsContext> CreateContext(
Window* target_window) override;
std::unique_ptr<GraphicsContext> CreateOffscreenContext() override;
protected:
private:
explicit VulkanProvider(Window* main_window);
bool Initialize();
std::unique_ptr<VulkanInstance> instance_;
std::unique_ptr<VulkanDevice> device_;
};
} // namespace vulkan

View File

@@ -1,811 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_swap_chain.h"
#include <mutex>
#include <string>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
#include "xenia/ui/vulkan/vulkan_instance.h"
#include "xenia/ui/vulkan/vulkan_util.h"
DEFINE_bool(vulkan_random_clear_color, false,
"Randomizes framebuffer clear color.", "Vulkan");
namespace xe {
namespace ui {
namespace vulkan {
VulkanSwapChain::VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device)
: instance_(instance), device_(device) {}
VulkanSwapChain::~VulkanSwapChain() { Shutdown(); }
VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
surface_ = surface;
VkResult status;
// Find a queue family that supports presentation.
VkBool32 surface_supported = false;
uint32_t queue_family_index = -1;
for (uint32_t i = 0;
i < device_->device_info().queue_family_properties.size(); i++) {
const VkQueueFamilyProperties& family_props =
device_->device_info().queue_family_properties[i];
if (!(family_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) ||
!(family_props.queueFlags & VK_QUEUE_TRANSFER_BIT)) {
continue;
}
status = vkGetPhysicalDeviceSurfaceSupportKHR(*device_, i, surface,
&surface_supported);
if (status == VK_SUCCESS && surface_supported == VK_TRUE) {
queue_family_index = i;
break;
}
}
if (!surface_supported) {
XELOGE(
"Physical device does not have a queue that supports "
"graphics/transfer/presentation!");
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
presentation_queue_ = device_->AcquireQueue(queue_family_index);
presentation_queue_family_ = queue_family_index;
if (!presentation_queue_) {
// That's okay, use the primary queue.
presentation_queue_ = device_->primary_queue();
presentation_queue_mutex_ = &device_->primary_queue_mutex();
presentation_queue_family_ = device_->queue_family_index();
if (!presentation_queue_) {
XELOGE("Failed to acquire swap chain presentation queue!");
return VK_ERROR_INITIALIZATION_FAILED;
}
}
// Query supported target formats.
uint32_t count = 0;
status =
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());
CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR");
if (status != VK_SUCCESS) {
return status;
}
// If the format list includes just one entry of VK_FORMAT_UNDEFINED the
// surface has no preferred format.
// Otherwise, at least one supported format will be returned.
assert_true(surface_formats.size() >= 1);
if (surface_formats.size() == 1 &&
surface_formats[0].format == VK_FORMAT_UNDEFINED) {
// Fallback to common RGBA.
surface_format_ = VK_FORMAT_R8G8B8A8_UNORM;
} else {
// Use first defined format.
surface_format_ = surface_formats[0].format;
}
// Query surface min/max/caps.
VkSurfaceCapabilitiesKHR surface_caps;
status = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_,
&surface_caps);
CheckResult(status, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Query surface properties so we can configure ourselves within bounds.
std::vector<VkPresentModeKHR> present_modes;
status = 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());
CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Calculate swapchain target dimensions.
VkExtent2D extent = surface_caps.currentExtent;
if (surface_caps.currentExtent.width == -1) {
assert_true(surface_caps.currentExtent.height == -1);
// Undefined extents, so we need to pick something.
XELOGI("Swap chain target surface extents undefined; guessing value");
extent.width = 1280;
extent.height = 720;
}
surface_width_ = extent.width;
surface_height_ = extent.height;
// Always prefer mailbox mode (non-tearing, low-latency).
// If it's not available we'll use immediate (tearing, low-latency).
// If not even that we fall back to FIFO, which sucks.
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (size_t i = 0; i < present_modes.size(); ++i) {
if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
// This is the best, so early-out.
present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
} else if (present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) {
present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
}
// Determine the number of images (1 + number queued).
uint32_t image_count = surface_caps.minImageCount + 1;
if (surface_caps.maxImageCount > 0 &&
image_count > surface_caps.maxImageCount) {
// Too many requested - use whatever we can.
XELOGI("Requested number of swapchain images ({}) exceeds maximum ({})",
image_count, surface_caps.maxImageCount);
image_count = surface_caps.maxImageCount;
}
// Always pass through whatever transform the surface started with (so long
// as it's supported).
VkSurfaceTransformFlagBitsKHR pre_transform = surface_caps.currentTransform;
VkSwapchainCreateInfoKHR create_info;
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.surface = surface_;
create_info.minImageCount = image_count;
create_info.imageFormat = surface_format_;
create_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
create_info.imageExtent.width = extent.width;
create_info.imageExtent.height = extent.height;
create_info.imageArrayLayers = 1;
create_info.imageUsage =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.queueFamilyIndexCount = 0;
create_info.pQueueFamilyIndices = nullptr;
create_info.preTransform = pre_transform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = nullptr;
XELOGVK("Creating swap chain:");
XELOGVK(" minImageCount = {}", create_info.minImageCount);
XELOGVK(" imageFormat = {}", to_string(create_info.imageFormat));
XELOGVK(" imageExtent = {} x {}", create_info.imageExtent.width,
create_info.imageExtent.height);
auto pre_transform_str = to_flags_string(create_info.preTransform);
XELOGVK(" preTransform = {}", pre_transform_str);
XELOGVK(" imageArrayLayers = {}", create_info.imageArrayLayers);
XELOGVK(" presentMode = {}", to_string(create_info.presentMode));
XELOGVK(" clipped = {}", create_info.clipped ? "true" : "false");
XELOGVK(" imageColorSpace = {}", to_string(create_info.imageColorSpace));
auto image_usage_flags_str = to_flags_string(
static_cast<VkImageUsageFlagBits>(create_info.imageUsage));
XELOGVK(" imageUsageFlags = {}", image_usage_flags_str);
XELOGVK(" imageSharingMode = {}", to_string(create_info.imageSharingMode));
XELOGVK(" queueFamilyCount = {}", create_info.queueFamilyIndexCount);
status = vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle);
if (status != VK_SUCCESS) {
XELOGE("Failed to create swapchain: {}", to_string(status));
return status;
}
// Create the pool used for transient buffers, so we can reset them all at
// once.
VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
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_);
CheckResult(status, "vkCreateCommandPool");
if (status != VK_SUCCESS) {
return status;
}
// Primary command buffer
VkCommandBufferAllocateInfo cmd_buffer_info;
cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd_buffer_info.pNext = nullptr;
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_);
CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// Make two command buffers we'll do all our primary rendering from.
VkCommandBuffer command_buffers[2];
cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
cmd_buffer_info.commandBufferCount = 2;
status =
vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers);
CheckResult(status, "vkCreateCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
render_cmd_buffer_ = command_buffers[0];
copy_cmd_buffer_ = command_buffers[1];
// Create the render pass used to draw to the swap chain.
// The actual framebuffer attached will depend on which image we are drawing
// into.
VkAttachmentDescription color_attachment;
color_attachment.flags = 0;
color_attachment.format = surface_format_;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; // CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_reference;
color_reference.attachment = 0;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference;
depth_reference.attachment = VK_ATTACHMENT_UNUSED;
depth_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkSubpassDescription render_subpass;
render_subpass.flags = 0;
render_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
render_subpass.inputAttachmentCount = 0;
render_subpass.pInputAttachments = nullptr;
render_subpass.colorAttachmentCount = 1;
render_subpass.pColorAttachments = &color_reference;
render_subpass.pResolveAttachments = nullptr;
render_subpass.pDepthStencilAttachment = &depth_reference;
render_subpass.preserveAttachmentCount = 0,
render_subpass.pPreserveAttachments = nullptr;
VkRenderPassCreateInfo render_pass_info;
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.pNext = nullptr;
render_pass_info.flags = 0;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment;
render_pass_info.subpassCount = 1;
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_);
CheckResult(status, "vkCreateRenderPass");
if (status != VK_SUCCESS) {
return status;
}
// Create a semaphore we'll use to synchronize with the swapchain.
VkSemaphoreCreateInfo semaphore_info;
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_);
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_);
CheckResult(status, "vkCreateSemaphore");
if (status != VK_SUCCESS) {
return status;
}
// Get images we will be presenting to.
// 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);
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
images.resize(actual_image_count);
status = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
images.data());
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
// Create all buffers.
buffers_.resize(images.size());
for (size_t i = 0; i < buffers_.size(); ++i) {
status = InitializeBuffer(&buffers_[i], images[i]);
if (status != VK_SUCCESS) {
XELOGE("Failed to initialize a swapchain buffer");
return status;
}
buffers_[i].image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
// Create a fence we'll use to wait for commands to finish.
VkFenceCreateInfo fence_create_info = {
VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
nullptr,
VK_FENCE_CREATE_SIGNALED_BIT,
};
status = vkCreateFence(*device_, &fence_create_info, nullptr,
&synchronization_fence_);
CheckResult(status, "vkGetSwapchainImagesKHR");
if (status != VK_SUCCESS) {
return status;
}
XELOGVK("Swap chain initialized successfully!");
return VK_SUCCESS;
}
VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer,
VkImage target_image) {
DestroyBuffer(buffer);
buffer->image = target_image;
VkResult status;
// Create an image view for the presentation image.
// This will be used as a framebuffer attachment.
VkImageViewCreateInfo image_view_info;
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.pNext = nullptr;
image_view_info.flags = 0;
image_view_info.image = buffer->image;
image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
image_view_info.format = surface_format_;
image_view_info.components.r = VK_COMPONENT_SWIZZLE_R;
image_view_info.components.g = VK_COMPONENT_SWIZZLE_G;
image_view_info.components.b = VK_COMPONENT_SWIZZLE_B;
image_view_info.components.a = VK_COMPONENT_SWIZZLE_A;
image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_view_info.subresourceRange.baseMipLevel = 0;
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);
CheckResult(status, "vkCreateImageView");
if (status != VK_SUCCESS) {
return status;
}
// Create the framebuffer used to render into this image.
VkImageView attachments[] = {buffer->image_view};
VkFramebufferCreateInfo framebuffer_info;
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.pNext = nullptr;
framebuffer_info.flags = 0;
framebuffer_info.renderPass = render_pass_;
framebuffer_info.attachmentCount =
static_cast<uint32_t>(xe::countof(attachments));
framebuffer_info.pAttachments = attachments;
framebuffer_info.width = surface_width_;
framebuffer_info.height = surface_height_;
framebuffer_info.layers = 1;
status = vkCreateFramebuffer(*device_, &framebuffer_info, nullptr,
&buffer->framebuffer);
CheckResult(status, "vkCreateFramebuffer");
if (status != VK_SUCCESS) {
return status;
}
return VK_SUCCESS;
}
void VulkanSwapChain::DestroyBuffer(Buffer* buffer) {
if (buffer->framebuffer) {
vkDestroyFramebuffer(*device_, buffer->framebuffer, nullptr);
buffer->framebuffer = nullptr;
}
if (buffer->image_view) {
vkDestroyImageView(*device_, buffer->image_view, nullptr);
buffer->image_view = nullptr;
}
// Image is taken care of by the presentation engine.
buffer->image = nullptr;
}
VkResult VulkanSwapChain::Reinitialize() {
// Hacky, but stash the surface so we can reuse it.
auto surface = surface_;
surface_ = nullptr;
Shutdown();
return Initialize(surface);
}
void VulkanSwapChain::WaitOnSemaphore(VkSemaphore sem) {
wait_semaphores_.push_back(sem);
}
void VulkanSwapChain::Shutdown() {
// TODO(benvanik): properly wait for a clean state.
for (auto& buffer : buffers_) {
DestroyBuffer(&buffer);
}
buffers_.clear();
VK_SAFE_DESTROY(vkDestroySemaphore, *device_, image_available_semaphore_,
nullptr);
VK_SAFE_DESTROY(vkDestroyRenderPass, *device_, render_pass_, nullptr);
if (copy_cmd_buffer_) {
vkFreeCommandBuffers(*device_, cmd_pool_, 1, &copy_cmd_buffer_);
copy_cmd_buffer_ = nullptr;
}
if (render_cmd_buffer_) {
vkFreeCommandBuffers(*device_, cmd_pool_, 1, &render_cmd_buffer_);
render_cmd_buffer_ = nullptr;
}
VK_SAFE_DESTROY(vkDestroyCommandPool, *device_, cmd_pool_, nullptr);
if (presentation_queue_) {
if (!presentation_queue_mutex_) {
// We own the queue and need to release it.
device_->ReleaseQueue(presentation_queue_, presentation_queue_family_);
}
presentation_queue_ = nullptr;
presentation_queue_mutex_ = nullptr;
presentation_queue_family_ = -1;
}
VK_SAFE_DESTROY(vkDestroyFence, *device_, synchronization_fence_, nullptr);
// 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);
}
VkResult VulkanSwapChain::Begin() {
wait_semaphores_.clear();
VkResult status;
// Wait for the last swap to finish.
status = vkWaitForFences(*device_, 1, &synchronization_fence_, VK_TRUE, -1);
if (status != VK_SUCCESS) {
return status;
}
status = 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_);
if (status != VK_SUCCESS) {
return status;
}
// Wait for the acquire semaphore to be signaled so that the following
// operations know they can start modifying the image.
VkSubmitInfo wait_submit_info;
wait_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
wait_submit_info.pNext = nullptr;
VkPipelineStageFlags wait_dst_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
wait_submit_info.waitSemaphoreCount = 1;
wait_submit_info.pWaitSemaphores = &image_available_semaphore_;
wait_submit_info.pWaitDstStageMask = &wait_dst_stage;
wait_submit_info.commandBufferCount = 0;
wait_submit_info.pCommandBuffers = nullptr;
wait_submit_info.signalSemaphoreCount = 1;
wait_submit_info.pSignalSemaphores = &image_usage_semaphore_;
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueueSubmit(presentation_queue_, 1, &wait_submit_info, nullptr);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}
if (status != VK_SUCCESS) {
return status;
}
// Reset all command buffers.
vkResetCommandBuffer(render_cmd_buffer_, 0);
vkResetCommandBuffer(copy_cmd_buffer_, 0);
auto& current_buffer = buffers_[current_buffer_index_];
// Build the command buffer that will execute all queued rendering buffers.
VkCommandBufferInheritanceInfo inherit_info;
inherit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inherit_info.pNext = nullptr;
inherit_info.renderPass = render_pass_;
inherit_info.subpass = 0;
inherit_info.framebuffer = current_buffer.framebuffer;
inherit_info.occlusionQueryEnable = VK_FALSE;
inherit_info.queryFlags = 0;
inherit_info.pipelineStatistics = 0;
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
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);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// 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);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// First: Issue a command to clear the render target.
VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
VkClearColorValue clear_color;
clear_color.float32[0] = 238 / 255.0f;
clear_color.float32[1] = 238 / 255.0f;
clear_color.float32[2] = 238 / 255.0f;
clear_color.float32[3] = 1.0f;
if (cvars::vulkan_random_clear_color) {
clear_color.float32[0] =
rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
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);
return VK_SUCCESS;
}
VkResult VulkanSwapChain::End() {
auto& current_buffer = buffers_[current_buffer_index_];
VkResult status;
status = vkEndCommandBuffer(render_cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
status = vkEndCommandBuffer(copy_cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// Build primary command buffer.
status = vkResetCommandBuffer(cmd_buffer_, 0);
CheckResult(status, "vkResetCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
begin_info.flags = 0;
begin_info.pInheritanceInfo = nullptr;
status = vkBeginCommandBuffer(cmd_buffer_, &begin_info);
CheckResult(status, "vkBeginCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
// Transition the image to a format we can copy to.
VkImageMemoryBarrier pre_image_copy_barrier;
pre_image_copy_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
pre_image_copy_barrier.pNext = nullptr;
pre_image_copy_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
pre_image_copy_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
pre_image_copy_barrier.oldLayout = current_buffer.image_layout;
pre_image_copy_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
pre_image_copy_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
pre_image_copy_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
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);
current_buffer.image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// Execute copy commands
vkCmdExecuteCommands(cmd_buffer_, 1, &copy_cmd_buffer_);
// Transition the image to a color attachment target for drawing.
VkImageMemoryBarrier pre_image_memory_barrier;
pre_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
pre_image_memory_barrier.pNext = nullptr;
pre_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
pre_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
pre_image_memory_barrier.image = current_buffer.image;
pre_image_memory_barrier.subresourceRange.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
pre_image_memory_barrier.subresourceRange.baseMipLevel = 0;
pre_image_memory_barrier.subresourceRange.levelCount = 1;
pre_image_memory_barrier.subresourceRange.baseArrayLayer = 0;
pre_image_memory_barrier.subresourceRange.layerCount = 1;
pre_image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
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);
current_buffer.image_layout = pre_image_memory_barrier.newLayout;
// Begin render pass.
VkRenderPassBeginInfo render_pass_begin_info;
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.pNext = nullptr;
render_pass_begin_info.renderPass = render_pass_;
render_pass_begin_info.framebuffer = current_buffer.framebuffer;
render_pass_begin_info.renderArea.offset.x = 0;
render_pass_begin_info.renderArea.offset.y = 0;
render_pass_begin_info.renderArea.extent.width = surface_width_;
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);
// Render commands.
vkCmdExecuteCommands(cmd_buffer_, 1, &render_cmd_buffer_);
// End render pass.
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?
VkImageMemoryBarrier post_image_memory_barrier;
post_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
post_image_memory_barrier.pNext = nullptr;
post_image_memory_barrier.srcAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
post_image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
post_image_memory_barrier.oldLayout = current_buffer.image_layout;
post_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
post_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
post_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
post_image_memory_barrier.image = current_buffer.image;
post_image_memory_barrier.subresourceRange.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
post_image_memory_barrier.subresourceRange.baseMipLevel = 0;
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);
current_buffer.image_layout = post_image_memory_barrier.newLayout;
status = vkEndCommandBuffer(cmd_buffer_);
CheckResult(status, "vkEndCommandBuffer");
if (status != VK_SUCCESS) {
return status;
}
std::vector<VkSemaphore> semaphores;
std::vector<VkPipelineStageFlags> wait_dst_stages;
for (size_t i = 0; i < wait_semaphores_.size(); i++) {
semaphores.push_back(wait_semaphores_[i]);
wait_dst_stages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
}
semaphores.push_back(image_usage_semaphore_);
wait_dst_stages.push_back(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
// Submit commands.
// Wait on the image usage semaphore (signaled when an image is available)
VkSubmitInfo render_submit_info;
render_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
render_submit_info.pNext = nullptr;
render_submit_info.waitSemaphoreCount = uint32_t(semaphores.size());
render_submit_info.pWaitSemaphores = semaphores.data();
render_submit_info.pWaitDstStageMask = wait_dst_stages.data();
render_submit_info.commandBufferCount = 1;
render_submit_info.pCommandBuffers = &cmd_buffer_;
render_submit_info.signalSemaphoreCount = 0;
render_submit_info.pSignalSemaphores = nullptr;
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueueSubmit(presentation_queue_, 1, &render_submit_info,
synchronization_fence_);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}
if (status != VK_SUCCESS) {
return status;
}
// Queue the present of our current image.
const VkSwapchainKHR swap_chains[] = {handle};
const uint32_t swap_chain_image_indices[] = {current_buffer_index_};
VkPresentInfoKHR present_info;
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.pNext = nullptr;
present_info.waitSemaphoreCount = 0;
present_info.pWaitSemaphores = nullptr;
present_info.swapchainCount = static_cast<uint32_t>(xe::countof(swap_chains));
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = swap_chain_image_indices;
present_info.pResults = nullptr;
if (presentation_queue_mutex_) {
presentation_queue_mutex_->lock();
}
status = vkQueuePresentKHR(presentation_queue_, &present_info);
if (presentation_queue_mutex_) {
presentation_queue_mutex_->unlock();
}
switch (status) {
case VK_SUCCESS:
break;
case VK_SUBOPTIMAL_KHR:
// We are not rendering at the right size - but the presentation engine
// will scale the output for us.
status = VK_SUCCESS;
break;
case VK_ERROR_OUT_OF_DATE_KHR:
// Lost presentation ability; need to recreate the swapchain.
// TODO(benvanik): recreate swapchain.
assert_always("Swapchain recreation not implemented");
break;
case VK_ERROR_DEVICE_LOST:
// Fatal. Device lost.
break;
default:
XELOGE("Failed to queue present: {}", to_string(status));
assert_always("Unexpected queue present failure");
}
return status;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,106 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_
#define XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
class VulkanDevice;
class VulkanInstance;
class VulkanSwapChain {
public:
VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device);
~VulkanSwapChain();
VkSwapchainKHR handle = nullptr;
operator VkSwapchainKHR() const { return handle; }
uint32_t surface_width() const { return surface_width_; }
uint32_t surface_height() const { return surface_height_; }
VkImage surface_image() const {
return buffers_[current_buffer_index_].image;
}
// Render pass used for compositing.
VkRenderPass render_pass() const { return render_pass_; }
// Render command buffer, active inside the render pass from Begin to End.
VkCommandBuffer render_cmd_buffer() const { return render_cmd_buffer_; }
// Copy commands, ran before the render command buffer.
VkCommandBuffer copy_cmd_buffer() const { return copy_cmd_buffer_; }
// Initializes the swap chain with the given WSI surface.
VkResult Initialize(VkSurfaceKHR surface);
// Reinitializes the swap chain with the initial surface.
// The surface will be retained but all other swap chain resources will be
// torn down and recreated with the new surface properties (size/etc).
VkResult Reinitialize();
// Waits on and signals a semaphore in this operation.
void WaitOnSemaphore(VkSemaphore sem);
// Begins the swap operation, preparing state for rendering.
VkResult Begin();
// Ends the swap operation, finalizing rendering and presenting the results.
VkResult End();
private:
struct Buffer {
VkImage image = nullptr;
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageView image_view = nullptr;
VkFramebuffer framebuffer = nullptr;
};
VkResult InitializeBuffer(Buffer* buffer, VkImage target_image);
void DestroyBuffer(Buffer* buffer);
// Safely releases all swap chain resources.
void Shutdown();
VulkanInstance* instance_ = nullptr;
VulkanDevice* device_ = nullptr;
VkFence synchronization_fence_ = nullptr;
VkQueue presentation_queue_ = nullptr;
std::mutex* presentation_queue_mutex_ = nullptr;
uint32_t presentation_queue_family_ = -1;
VkSurfaceKHR surface_ = nullptr;
uint32_t surface_width_ = 0;
uint32_t surface_height_ = 0;
VkFormat surface_format_ = VK_FORMAT_UNDEFINED;
VkCommandPool cmd_pool_ = nullptr;
VkCommandBuffer cmd_buffer_ = nullptr;
VkCommandBuffer copy_cmd_buffer_ = nullptr;
VkCommandBuffer render_cmd_buffer_ = nullptr;
VkRenderPass render_pass_ = nullptr;
VkSemaphore image_available_semaphore_ = nullptr;
VkSemaphore image_usage_semaphore_ = nullptr;
uint32_t current_buffer_index_ = 0;
std::vector<Buffer> buffers_;
std::vector<VkSemaphore> wait_semaphores_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_

View File

@@ -1,504 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_util.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
// Implement AMD's VMA here.
#define VMA_IMPLEMENTATION
#include "xenia/ui/vulkan/vulkan_mem_alloc.h"
namespace xe {
namespace ui {
namespace vulkan {
uint32_t Version::Make(uint32_t major, uint32_t minor, uint32_t patch) {
return VK_MAKE_VERSION(major, minor, patch);
}
Version Version::Parse(uint32_t value) {
Version version;
version.major = VK_VERSION_MAJOR(value);
version.minor = VK_VERSION_MINOR(value);
version.patch = VK_VERSION_PATCH(value);
version.pretty_string =
fmt::format("{}.{}.{}", version.major, version.minor, version.patch);
return version;
}
const char* to_string(VkFormat format) {
switch (format) {
#define STR(r) \
case r: \
return #r
STR(VK_FORMAT_UNDEFINED);
STR(VK_FORMAT_R4G4_UNORM_PACK8);
STR(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
STR(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
STR(VK_FORMAT_R5G6B5_UNORM_PACK16);
STR(VK_FORMAT_B5G6R5_UNORM_PACK16);
STR(VK_FORMAT_R5G5B5A1_UNORM_PACK16);
STR(VK_FORMAT_B5G5R5A1_UNORM_PACK16);
STR(VK_FORMAT_A1R5G5B5_UNORM_PACK16);
STR(VK_FORMAT_R8_UNORM);
STR(VK_FORMAT_R8_SNORM);
STR(VK_FORMAT_R8_USCALED);
STR(VK_FORMAT_R8_SSCALED);
STR(VK_FORMAT_R8_UINT);
STR(VK_FORMAT_R8_SINT);
STR(VK_FORMAT_R8_SRGB);
STR(VK_FORMAT_R8G8_UNORM);
STR(VK_FORMAT_R8G8_SNORM);
STR(VK_FORMAT_R8G8_USCALED);
STR(VK_FORMAT_R8G8_SSCALED);
STR(VK_FORMAT_R8G8_UINT);
STR(VK_FORMAT_R8G8_SINT);
STR(VK_FORMAT_R8G8_SRGB);
STR(VK_FORMAT_R8G8B8_UNORM);
STR(VK_FORMAT_R8G8B8_SNORM);
STR(VK_FORMAT_R8G8B8_USCALED);
STR(VK_FORMAT_R8G8B8_SSCALED);
STR(VK_FORMAT_R8G8B8_UINT);
STR(VK_FORMAT_R8G8B8_SINT);
STR(VK_FORMAT_R8G8B8_SRGB);
STR(VK_FORMAT_B8G8R8_UNORM);
STR(VK_FORMAT_B8G8R8_SNORM);
STR(VK_FORMAT_B8G8R8_USCALED);
STR(VK_FORMAT_B8G8R8_SSCALED);
STR(VK_FORMAT_B8G8R8_UINT);
STR(VK_FORMAT_B8G8R8_SINT);
STR(VK_FORMAT_B8G8R8_SRGB);
STR(VK_FORMAT_R8G8B8A8_UNORM);
STR(VK_FORMAT_R8G8B8A8_SNORM);
STR(VK_FORMAT_R8G8B8A8_USCALED);
STR(VK_FORMAT_R8G8B8A8_SSCALED);
STR(VK_FORMAT_R8G8B8A8_UINT);
STR(VK_FORMAT_R8G8B8A8_SINT);
STR(VK_FORMAT_R8G8B8A8_SRGB);
STR(VK_FORMAT_B8G8R8A8_UNORM);
STR(VK_FORMAT_B8G8R8A8_SNORM);
STR(VK_FORMAT_B8G8R8A8_USCALED);
STR(VK_FORMAT_B8G8R8A8_SSCALED);
STR(VK_FORMAT_B8G8R8A8_UINT);
STR(VK_FORMAT_B8G8R8A8_SINT);
STR(VK_FORMAT_B8G8R8A8_SRGB);
STR(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
STR(VK_FORMAT_A8B8G8R8_SNORM_PACK32);
STR(VK_FORMAT_A8B8G8R8_USCALED_PACK32);
STR(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);
STR(VK_FORMAT_A8B8G8R8_UINT_PACK32);
STR(VK_FORMAT_A8B8G8R8_SINT_PACK32);
STR(VK_FORMAT_A8B8G8R8_SRGB_PACK32);
STR(VK_FORMAT_A2R10G10B10_UNORM_PACK32);
STR(VK_FORMAT_A2R10G10B10_SNORM_PACK32);
STR(VK_FORMAT_A2R10G10B10_USCALED_PACK32);
STR(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);
STR(VK_FORMAT_A2R10G10B10_UINT_PACK32);
STR(VK_FORMAT_A2R10G10B10_SINT_PACK32);
STR(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
STR(VK_FORMAT_A2B10G10R10_SNORM_PACK32);
STR(VK_FORMAT_A2B10G10R10_USCALED_PACK32);
STR(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);
STR(VK_FORMAT_A2B10G10R10_UINT_PACK32);
STR(VK_FORMAT_A2B10G10R10_SINT_PACK32);
STR(VK_FORMAT_R16_UNORM);
STR(VK_FORMAT_R16_SNORM);
STR(VK_FORMAT_R16_USCALED);
STR(VK_FORMAT_R16_SSCALED);
STR(VK_FORMAT_R16_UINT);
STR(VK_FORMAT_R16_SINT);
STR(VK_FORMAT_R16_SFLOAT);
STR(VK_FORMAT_R16G16_UNORM);
STR(VK_FORMAT_R16G16_SNORM);
STR(VK_FORMAT_R16G16_USCALED);
STR(VK_FORMAT_R16G16_SSCALED);
STR(VK_FORMAT_R16G16_UINT);
STR(VK_FORMAT_R16G16_SINT);
STR(VK_FORMAT_R16G16_SFLOAT);
STR(VK_FORMAT_R16G16B16_UNORM);
STR(VK_FORMAT_R16G16B16_SNORM);
STR(VK_FORMAT_R16G16B16_USCALED);
STR(VK_FORMAT_R16G16B16_SSCALED);
STR(VK_FORMAT_R16G16B16_UINT);
STR(VK_FORMAT_R16G16B16_SINT);
STR(VK_FORMAT_R16G16B16_SFLOAT);
STR(VK_FORMAT_R16G16B16A16_UNORM);
STR(VK_FORMAT_R16G16B16A16_SNORM);
STR(VK_FORMAT_R16G16B16A16_USCALED);
STR(VK_FORMAT_R16G16B16A16_SSCALED);
STR(VK_FORMAT_R16G16B16A16_UINT);
STR(VK_FORMAT_R16G16B16A16_SINT);
STR(VK_FORMAT_R16G16B16A16_SFLOAT);
STR(VK_FORMAT_R32_UINT);
STR(VK_FORMAT_R32_SINT);
STR(VK_FORMAT_R32_SFLOAT);
STR(VK_FORMAT_R32G32_UINT);
STR(VK_FORMAT_R32G32_SINT);
STR(VK_FORMAT_R32G32_SFLOAT);
STR(VK_FORMAT_R32G32B32_UINT);
STR(VK_FORMAT_R32G32B32_SINT);
STR(VK_FORMAT_R32G32B32_SFLOAT);
STR(VK_FORMAT_R32G32B32A32_UINT);
STR(VK_FORMAT_R32G32B32A32_SINT);
STR(VK_FORMAT_R32G32B32A32_SFLOAT);
STR(VK_FORMAT_R64_UINT);
STR(VK_FORMAT_R64_SINT);
STR(VK_FORMAT_R64_SFLOAT);
STR(VK_FORMAT_R64G64_UINT);
STR(VK_FORMAT_R64G64_SINT);
STR(VK_FORMAT_R64G64_SFLOAT);
STR(VK_FORMAT_R64G64B64_UINT);
STR(VK_FORMAT_R64G64B64_SINT);
STR(VK_FORMAT_R64G64B64_SFLOAT);
STR(VK_FORMAT_R64G64B64A64_UINT);
STR(VK_FORMAT_R64G64B64A64_SINT);
STR(VK_FORMAT_R64G64B64A64_SFLOAT);
STR(VK_FORMAT_B10G11R11_UFLOAT_PACK32);
STR(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);
STR(VK_FORMAT_D16_UNORM);
STR(VK_FORMAT_X8_D24_UNORM_PACK32);
STR(VK_FORMAT_D32_SFLOAT);
STR(VK_FORMAT_S8_UINT);
STR(VK_FORMAT_D16_UNORM_S8_UINT);
STR(VK_FORMAT_D24_UNORM_S8_UINT);
STR(VK_FORMAT_D32_SFLOAT_S8_UINT);
STR(VK_FORMAT_BC1_RGB_UNORM_BLOCK);
STR(VK_FORMAT_BC1_RGB_SRGB_BLOCK);
STR(VK_FORMAT_BC1_RGBA_UNORM_BLOCK);
STR(VK_FORMAT_BC1_RGBA_SRGB_BLOCK);
STR(VK_FORMAT_BC2_UNORM_BLOCK);
STR(VK_FORMAT_BC2_SRGB_BLOCK);
STR(VK_FORMAT_BC3_UNORM_BLOCK);
STR(VK_FORMAT_BC3_SRGB_BLOCK);
STR(VK_FORMAT_BC4_UNORM_BLOCK);
STR(VK_FORMAT_BC4_SNORM_BLOCK);
STR(VK_FORMAT_BC5_UNORM_BLOCK);
STR(VK_FORMAT_BC5_SNORM_BLOCK);
STR(VK_FORMAT_BC6H_UFLOAT_BLOCK);
STR(VK_FORMAT_BC6H_SFLOAT_BLOCK);
STR(VK_FORMAT_BC7_UNORM_BLOCK);
STR(VK_FORMAT_BC7_SRGB_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK);
STR(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK);
STR(VK_FORMAT_EAC_R11_UNORM_BLOCK);
STR(VK_FORMAT_EAC_R11_SNORM_BLOCK);
STR(VK_FORMAT_EAC_R11G11_UNORM_BLOCK);
STR(VK_FORMAT_EAC_R11G11_SNORM_BLOCK);
STR(VK_FORMAT_ASTC_4x4_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_5x4_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_5x4_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_5x5_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_5x5_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_6x5_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_6x5_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_6x6_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_6x6_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_8x5_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_8x5_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_8x6_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_8x6_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_8x8_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_8x8_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_10x5_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_10x5_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_10x6_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_10x6_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_10x8_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_10x8_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_10x10_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_10x10_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_12x10_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_12x10_SRGB_BLOCK);
STR(VK_FORMAT_ASTC_12x12_UNORM_BLOCK);
STR(VK_FORMAT_ASTC_12x12_SRGB_BLOCK);
#undef STR
default:
return "UNKNOWN_FORMAT";
}
}
const char* to_string(VkPhysicalDeviceType type) {
switch (type) {
#define STR(r) \
case r: \
return #r
STR(VK_PHYSICAL_DEVICE_TYPE_OTHER);
STR(VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
STR(VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
STR(VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU);
STR(VK_PHYSICAL_DEVICE_TYPE_CPU);
#undef STR
default:
return "UNKNOWN_DEVICE";
}
}
const char* to_string(VkSharingMode sharing_mode) {
switch (sharing_mode) {
#define STR(r) \
case r: \
return #r
STR(VK_SHARING_MODE_EXCLUSIVE);
STR(VK_SHARING_MODE_CONCURRENT);
#undef STR
default:
return "UNKNOWN_SHARING_MODE";
}
}
const char* to_string(VkResult result) {
switch (result) {
#define STR(r) \
case r: \
return #r
STR(VK_SUCCESS);
STR(VK_NOT_READY);
STR(VK_TIMEOUT);
STR(VK_EVENT_SET);
STR(VK_EVENT_RESET);
STR(VK_INCOMPLETE);
STR(VK_ERROR_OUT_OF_HOST_MEMORY);
STR(VK_ERROR_OUT_OF_DEVICE_MEMORY);
STR(VK_ERROR_INITIALIZATION_FAILED);
STR(VK_ERROR_DEVICE_LOST);
STR(VK_ERROR_MEMORY_MAP_FAILED);
STR(VK_ERROR_LAYER_NOT_PRESENT);
STR(VK_ERROR_EXTENSION_NOT_PRESENT);
STR(VK_ERROR_FEATURE_NOT_PRESENT);
STR(VK_ERROR_INCOMPATIBLE_DRIVER);
STR(VK_ERROR_TOO_MANY_OBJECTS);
STR(VK_ERROR_FORMAT_NOT_SUPPORTED);
STR(VK_ERROR_SURFACE_LOST_KHR);
STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
STR(VK_SUBOPTIMAL_KHR);
STR(VK_ERROR_OUT_OF_DATE_KHR);
STR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
STR(VK_ERROR_VALIDATION_FAILED_EXT);
STR(VK_ERROR_INVALID_SHADER_NV);
STR(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);
STR(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
#undef STR
default:
return "UNKNOWN_RESULT";
}
}
std::string to_flags_string(VkImageUsageFlagBits flags) {
std::string result;
#define OR_FLAG(f) \
if (flags & f) { \
if (!result.empty()) { \
result += " | "; \
} \
result += #f; \
}
OR_FLAG(VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
OR_FLAG(VK_IMAGE_USAGE_TRANSFER_DST_BIT);
OR_FLAG(VK_IMAGE_USAGE_SAMPLED_BIT);
OR_FLAG(VK_IMAGE_USAGE_STORAGE_BIT);
OR_FLAG(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
OR_FLAG(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
OR_FLAG(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
OR_FLAG(VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
#undef OR_FLAG
return result;
}
std::string to_flags_string(VkFormatFeatureFlagBits flags) {
std::string result;
#define OR_FLAG(f) \
if (flags & f) { \
if (!result.empty()) { \
result += " | "; \
} \
result += #f; \
}
OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
OR_FLAG(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
OR_FLAG(VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT);
OR_FLAG(VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT);
OR_FLAG(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT);
OR_FLAG(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT);
OR_FLAG(VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
OR_FLAG(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
OR_FLAG(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
OR_FLAG(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
OR_FLAG(VK_FORMAT_FEATURE_BLIT_SRC_BIT);
OR_FLAG(VK_FORMAT_FEATURE_BLIT_DST_BIT);
OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG);
OR_FLAG(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR);
OR_FLAG(VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR);
OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT);
#undef OR_FLAG
return result;
}
std::string to_flags_string(VkSurfaceTransformFlagBitsKHR flags) {
std::string result;
#define OR_FLAG(f) \
if (flags & f) { \
if (!result.empty()) { \
result += " | "; \
} \
result += #f; \
}
OR_FLAG(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR);
OR_FLAG(VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR);
#undef OR_FLAG
return result;
}
const char* to_string(VkColorSpaceKHR color_space) {
switch (color_space) {
#define STR(r) \
case r: \
return #r
STR(VK_COLORSPACE_SRGB_NONLINEAR_KHR);
#undef STR
default:
return "UNKNOWN_COLORSPACE";
}
}
const char* to_string(VkPresentModeKHR present_mode) {
switch (present_mode) {
#define STR(r) \
case r: \
return #r
STR(VK_PRESENT_MODE_IMMEDIATE_KHR);
STR(VK_PRESENT_MODE_MAILBOX_KHR);
STR(VK_PRESENT_MODE_FIFO_KHR);
STR(VK_PRESENT_MODE_FIFO_RELAXED_KHR);
#undef STR
default:
return "UNKNOWN_PRESENT_MODE";
}
}
void FatalVulkanError(std::string error) {
xe::FatalError(
error +
"\n\n"
"Ensure you have the latest drivers for your GPU and that it supports "
"Vulkan.\n"
"\n"
"See https://xenia.jp/faq/ for more information and a list of supported "
"GPUs.");
}
void CheckResult(VkResult result, const char* action) {
if (result) {
XELOGE("Vulkan check: {} returned {}", action, to_string(result));
}
assert_true(result == VK_SUCCESS, action);
}
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<LayerInfo>& layer_infos) {
bool any_missing = false;
std::vector<const char*> enabled_layers;
for (auto& requirement : requirements) {
bool found = false;
for (size_t j = 0; j < layer_infos.size(); ++j) {
auto layer_name = layer_infos[j].properties.layerName;
auto layer_version =
Version::Parse(layer_infos[j].properties.specVersion);
if (requirement.name == layer_name) {
found = true;
if (requirement.min_version > layer_infos[j].properties.specVersion) {
if (requirement.is_optional) {
XELOGVK("- optional layer {} ({}) version mismatch", layer_name,
layer_version.pretty_string);
continue;
}
XELOGE("ERROR: required layer {} ({}) version mismatch", layer_name,
layer_version.pretty_string);
any_missing = true;
break;
}
XELOGVK("- enabling layer {} ({})", layer_name,
layer_version.pretty_string);
enabled_layers.push_back(layer_name);
break;
}
}
if (!found) {
if (requirement.is_optional) {
XELOGVK("- optional layer {} not found", requirement.name);
} else {
XELOGE("ERROR: required layer {} not found", requirement.name);
any_missing = true;
}
}
}
return {!any_missing, enabled_layers};
}
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<VkExtensionProperties>& extension_properties) {
bool any_missing = false;
std::vector<const char*> enabled_extensions;
for (auto& requirement : requirements) {
bool found = false;
for (size_t j = 0; j < extension_properties.size(); ++j) {
auto extension_name = extension_properties[j].extensionName;
auto extension_version =
Version::Parse(extension_properties[j].specVersion);
if (requirement.name == extension_name) {
found = true;
if (requirement.min_version > extension_properties[j].specVersion) {
if (requirement.is_optional) {
XELOGVK("- optional extension {} ({}) version mismatch",
extension_name, extension_version.pretty_string);
continue;
}
XELOGE("ERROR: required extension {} ({}) version mismatch",
extension_name, extension_version.pretty_string);
any_missing = true;
break;
}
XELOGVK("- enabling extension {} ({})", extension_name,
extension_version.pretty_string);
enabled_extensions.push_back(extension_name);
break;
}
}
if (!found) {
if (requirement.is_optional) {
XELOGVK("- optional extension {} not found", requirement.name);
} else {
XELOGE("ERROR: required extension {} not found", requirement.name);
any_missing = true;
}
}
}
return {!any_missing, enabled_extensions};
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -1,136 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_VULKAN_VULKAN_UTIL_H_
#define XENIA_UI_VULKAN_VULKAN_UTIL_H_
#include <memory>
#include <string>
#include <vector>
#include "xenia/ui/vulkan/vulkan.h"
namespace xe {
namespace ui {
class Window;
} // namespace ui
} // namespace xe
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_);
}
~Fence() {
vkDestroyFence(device_, fence_, nullptr);
fence_ = nullptr;
}
VkResult status() const { return vkGetFenceStatus(device_, fence_); }
VkFence fence() const { return fence_; }
operator VkFence() const { return fence_; }
private:
VkDevice device_;
VkFence fence_ = nullptr;
};
struct Version {
uint32_t major;
uint32_t minor;
uint32_t patch;
std::string pretty_string;
static uint32_t Make(uint32_t major, uint32_t minor, uint32_t patch);
static Version Parse(uint32_t value);
};
const char* to_string(VkFormat format);
const char* to_string(VkPhysicalDeviceType type);
const char* to_string(VkSharingMode sharing_mode);
const char* to_string(VkResult result);
std::string to_flags_string(VkImageUsageFlagBits flags);
std::string to_flags_string(VkFormatFeatureFlagBits flags);
std::string to_flags_string(VkSurfaceTransformFlagBitsKHR flags);
const char* to_string(VkColorSpaceKHR color_space);
const char* to_string(VkPresentModeKHR present_mode);
// Throws a fatal error with some Vulkan help text.
void FatalVulkanError(std::string error);
// Logs and assets expecting the result to be VK_SUCCESS.
void CheckResult(VkResult result, const char* action);
struct LayerInfo {
VkLayerProperties properties;
std::vector<VkExtensionProperties> extensions;
};
struct DeviceInfo {
VkPhysicalDevice handle;
VkPhysicalDeviceProperties properties;
VkPhysicalDeviceFeatures features;
VkPhysicalDeviceMemoryProperties memory_properties;
std::vector<VkQueueFamilyProperties> queue_family_properties;
std::vector<LayerInfo> layers;
std::vector<VkExtensionProperties> extensions;
};
// Defines a requirement for a layer or extension, used to both verify and
// enable them on initialization.
struct Requirement {
// Layer or extension name.
std::string name;
// Minimum required spec version of the layer or extension.
uint32_t min_version;
// True if the requirement is optional (will not cause verification to fail).
bool is_optional;
};
// Gets a list of enabled layer names based on the given layer requirements and
// available layer info.
// Returns a boolean indicating whether all required layers are present.
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<LayerInfo>& layer_infos);
// Gets a list of enabled extension names based on the given extension
// requirements and available extensions.
// Returns a boolean indicating whether all required extensions are present.
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<VkExtensionProperties>& extension_properties);
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_UTIL_H_

View File

@@ -1,29 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <string>
#include <vector>
#include "xenia/base/main.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
int window_demo_main(const std::vector<std::string>& args);
std::unique_ptr<GraphicsProvider> CreateDemoGraphicsProvider(Window* window) {
return xe::ui::vulkan::VulkanProvider::Create(window);
}
} // namespace ui
} // namespace xe
DEFINE_ENTRY_POINT("xenia-ui-window-vulkan-demo", xe::ui::window_demo_main, "");