[Vulkan] Remove old Vulkan code, change shaders directory, create empty Vulkan backend
This commit is contained in:
@@ -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 = ℑ
|
||||
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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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",
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ©_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, ¤t_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, ©_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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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, "");
|
||||
Reference in New Issue
Block a user