Initial vulkan context and immediate drawer.

Extremely rough, just checking in so DrChat can snoop.
This commit is contained in:
Ben Vanik
2016-02-17 17:42:25 -08:00
parent d52d2c4837
commit 9f52f2e819
64 changed files with 24379 additions and 935 deletions

View File

@@ -24,7 +24,7 @@ void SpirvShaderTranslator::StartTranslation() {
auto fn = e.MakeMainEntry();
auto float_1_0 = e.MakeFloatConstant(1.0f);
auto acos = e.CreateGlslStd450InstructionCall(
spv::Decoration::Invariant, e.MakeFloatType(32), spv::GLSLstd450::Acos,
spv::Decoration::Invariant, e.MakeFloatType(32), spv::GLSLstd450::kAcos,
{float_1_0});
e.MakeReturn(true);
}
@@ -188,7 +188,7 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
if (op.is_absolute_value) {
current_value_id = e.CreateGlslStd450InstructionCall(
spv::Decoration::RelaxedPrecision, current_type_id,
spv::GLSLstd450::FAbs, {current_value_id});
spv::GLSLstd450::kFAbs, {current_value_id});
}
if (op.is_negated) {
current_value_id =

View File

@@ -9,7 +9,7 @@
#include "xenia/ui/spirv/spirv_assembler.h"
#include "third_party/spirv-tools/include/libspirv/libspirv.h"
#include "third_party/spirv-tools/include/spirv-tools/libspirv.h"
#include "xenia/base/logging.h"
namespace xe {

View File

@@ -9,7 +9,7 @@
#include "xenia/ui/spirv/spirv_disassembler.h"
#include "third_party/spirv-tools/include/libspirv/libspirv.h"
#include "third_party/spirv-tools/include/spirv-tools/libspirv.h"
#include "xenia/base/logging.h"
namespace xe {

View File

@@ -10,8 +10,8 @@
#ifndef XENIA_UI_SPIRV_SPIRV_UTIL_H_
#define XENIA_UI_SPIRV_SPIRV_UTIL_H_
#include "third_party/spirv/GLSL.std.450.h"
#include "third_party/spirv/spirv.h"
#include "third_party/spirv/GLSL.std.450.hpp11"
#include "third_party/spirv/spirv.hpp11"
// Forward declarations from SPIRV-Tools so we don't pollute /so/ much.
struct spv_binary_t;

View File

@@ -0,0 +1,53 @@
project_root = "../../../.."
include(project_root.."/tools/build")
group("src")
project("xenia-ui-vulkan")
uuid("4933d81e-1c2c-4d5d-b104-3c0eb9dc2f00")
kind("StaticLib")
language("C++")
links({
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
project_root.."/third_party/vulkan/",
})
local_platform_files()
removefiles({"*_demo.cc"})
group("demos")
project("xenia-ui-window-vulkan-demo")
uuid("97598f13-3177-454c-8e58-c59e2b6ede27")
kind("WindowedApp")
language("C++")
links({
"gflags",
"imgui",
"vulkan-loader",
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
})
flags({
"WinMain", -- Use WinMain instead of main.
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
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,
})

View File

@@ -0,0 +1,2 @@
glslangValidator -V immediate.vert -o immediate.vert.spv
glslangValidator -V immediate.frag -o immediate.frag.spv

View File

@@ -0,0 +1,23 @@
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
mat4 projection_matrix;
int restrict_texture_samples;
} push_constants;
layout(set = 0, binding = 0) uniform sampler2D texture_sampler;
layout(location = 0) in vec2 vtx_uv;
layout(location = 1) in vec4 vtx_color;
layout(location = 0) out vec4 out_color;
void main() {
out_color = vtx_color;
if (push_constants.restrict_texture_samples == 0 || vtx_uv.x <= 1.0) {
vec4 tex_color = texture(texture_sampler, vtx_uv);
out_color *= tex_color;
// TODO(benvanik): microprofiler shadows.
}
}

View File

@@ -0,0 +1,124 @@
const uint8_t immediate_frag_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x35, 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,
0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 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,
0x10, 0x00, 0x00, 0x00, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73,
0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
0x10, 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, 0x06, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00,
0x01, 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,
0x12, 0x00, 0x00, 0x00, 0x70, 0x75, 0x73, 0x68, 0x5F, 0x63, 0x6F, 0x6E,
0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F,
0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x2E, 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, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x2E, 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,
0x18, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x2B, 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, 0x2C, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x2E, 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, 0x3B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x07, 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, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0xAA, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0xBC, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00,
0xF5, 0x00, 0x07, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x28, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

Binary file not shown.

View File

@@ -0,0 +1,21 @@
#version 450 core
precision highp float;
layout(push_constant) uniform PushConstants {
mat4 projection_matrix;
int restrict_texture_samples;
} push_constants;
layout(location = 0) in vec2 in_pos;
layout(location = 1) in vec2 in_uv;
layout(location = 2) in vec4 in_color;
layout(location = 0) out vec2 vtx_uv;
layout(location = 1) out vec4 vtx_color;
void main() {
gl_Position = push_constants.projection_matrix * vec4(in_pos.xy, 0.0, 1.0);
gl_Position.y = -gl_Position.y;
vtx_uv = in_uv;
vtx_color = in_color;
}

View File

@@ -0,0 +1,136 @@
const uint8_t immediate_vert_spv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00,
0x11, 0x00, 0x02, 0x00, 0x21, 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, 0x06, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 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,
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, 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 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, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0E, 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, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x27, 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.

View File

@@ -0,0 +1,12 @@
/**
******************************************************************************
* 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.");

View File

@@ -0,0 +1,34 @@
/**
******************************************************************************
* 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 <gflags/gflags.h>
#include "xenia/base/platform.h"
#if XE_PLATFORM_WIN32
#define VK_USE_PLATFORM_WIN32_KHR 1
#else
#error Platform not yet supported.
#endif // XE_PLATFORM_WIN32
// We are statically linked with the loader, so use function prototypes.
#define VK_PROTOTYPES
#include "third_party/vulkan/vulkan.h"
// NOTE: header order matters here, unfortunately:
#include "third_party/vulkan/vk_lunarg_debug_marker.h"
#define XELOGVK XELOGI
DECLARE_bool(vulkan_validation);
#endif // XENIA_UI_VULKAN_VULKAN_H_

View File

@@ -0,0 +1,148 @@
/**
******************************************************************************
* 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_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"
namespace xe {
namespace ui {
namespace vulkan {
VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
: GraphicsContext(provider, target_window) {}
VulkanContext::~VulkanContext() {
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
vkQueueWaitIdle(device->primary_queue());
immediate_drawer_.reset();
swap_chain_.reset();
if (cmd_pool_) {
vkDestroyCommandPool(*device, cmd_pool_, nullptr);
}
}
bool VulkanContext::Initialize() {
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
// All context-specific commands will be allocated from this.
// We may want to have additional pools for different rendering subsystems.
VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = nullptr;
cmd_pool_info.queueFamilyIndex = device->queue_family_index();
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
auto err = vkCreateCommandPool(*device, &cmd_pool_info, nullptr, &cmd_pool_);
CheckResult(err, "vkCreateCommandPool");
if (target_window_) {
// Create swap chain used to present to the window.
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());
err = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info, nullptr,
&surface);
CheckResult(err, "vkCreateWin32SurfaceKHR");
#else
#error Platform not yet implemented.
#endif // XE_PLATFORM_WIN32
swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(),
provider->device());
if (!swap_chain_->Initialize(surface)) {
XELOGE("Unable to initialize swap chain");
vkDestroySurfaceKHR(*provider->instance(), surface, nullptr);
return false;
}
// Only initialize immediate mode drawer if we are not an offscreen context.
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
}
return true;
}
ImmediateDrawer* VulkanContext::immediate_drawer() {
return immediate_drawer_.get();
}
VulkanInstance* VulkanContext::instance() const {
return static_cast<VulkanProvider*>(provider_)->instance();
}
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();
// Acquire the next image and set it up for use.
swap_chain_->Begin();
// TODO(benvanik): use a fence instead? May not be possible with target image.
auto err = vkQueueWaitIdle(device->primary_queue());
CheckResult(err, "vkQueueWaitIdle");
}
void VulkanContext::EndSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
// Notify the presentation engine the image is ready.
// The contents must be in a coherent state.
swap_chain_->End();
// Wait until the queue is idle.
// TODO(benvanik): is this required?
auto err = vkQueueWaitIdle(device->primary_queue());
CheckResult(err, "vkQueueWaitIdle");
}
std::unique_ptr<RawImage> VulkanContext::Capture() {
assert_always();
return nullptr;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,63 @@
/**
******************************************************************************
* 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_CONTEXT_H_
#define XENIA_UI_VULKAN_VULKAN_CONTEXT_H_
#include <memory>
#include "xenia/ui/graphics_context.h"
#include "xenia/ui/vulkan/vulkan.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;
void BeginSwap() override;
void EndSwap() override;
std::unique_ptr<RawImage> Capture() override;
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_;
VkCommandPool cmd_pool_ = nullptr;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_CONTEXT_H_

View File

@@ -0,0 +1,222 @@
/**
******************************************************************************
* 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_device.h"
#include <gflags/gflags.h>
#include <cinttypes>
#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_immediate_drawer.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 (FLAGS_vulkan_validation) {
/*DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects",
Version::Make(0, 0, 0), true);*/
DeclareRequiredLayer("VK_LAYER_LUNARG_threading", Version::Make(0, 0, 0),
true);
/*DeclareRequiredLayer("VK_LAYER_LUNARG_mem_tracker", 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_param_checker",
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);
}
}
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);
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) {
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(geometryShader);
ENABLE_AND_EXPECT(depthClamp);
ENABLE_AND_EXPECT(alphaToOne);
ENABLE_AND_EXPECT(multiViewport);
// 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 (!device_info.queue_family_supports_present[i]) {
// Can't present from this queue, so ignore it.
continue;
}
if (queue_flags & VK_QUEUE_GRAPHICS_BIT) {
// Can do graphics and present - good!
ideal_queue_family_index = static_cast<uint32_t>(i);
// TODO(benvanik): pick a higher queue count?
queue_count = 1;
break;
}
}
if (ideal_queue_family_index == UINT_MAX) {
FatalVulkanError(
"No queue families available that can both do graphics and present");
return false;
}
VkDeviceQueueCreateInfo queue_info;
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.pNext = nullptr;
queue_info.flags = 0;
queue_info.queueFamilyIndex = ideal_queue_family_index;
queue_info.queueCount = queue_count;
std::vector<float> queue_priorities(queue_count);
queue_info.pQueuePriorities = queue_priorities.data();
VkDeviceCreateInfo create_info;
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.queueCreateInfoCount = 1;
create_info.pQueueCreateInfos = &queue_info;
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;
}
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_);
XELOGVK("Device initialized successfully!");
return true;
}
VkDeviceMemory VulkanDevice::AllocateMemory(
const VkMemoryRequirements& requirements, VkFlags required_properties) {
// Search memory types to find one matching our requirements and our
// properties.
uint32_t type_index = UINT_MAX;
for (uint32_t i = 0; i < device_info_.memory_properties.memoryTypeCount;
++i) {
const auto& memory_type = device_info_.memory_properties.memoryTypes[i];
if (((requirements.memoryTypeBits >> i) & 1) == 1) {
// Type is available for use; check for a match on properties.
if ((memory_type.propertyFlags & required_properties) ==
required_properties) {
type_index = i;
break;
}
}
}
if (type_index == UINT_MAX) {
XELOGE("Unable to find a matching memory type");
return nullptr;
}
// Allocate the memory.
VkMemoryAllocateInfo memory_info;
memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_info.pNext = nullptr;
memory_info.allocationSize = requirements.size;
memory_info.memoryTypeIndex = type_index;
VkDeviceMemory memory = nullptr;
auto err = vkAllocateMemory(handle, &memory_info, nullptr, &memory);
CheckResult(err, "vkAllocateMemory");
return memory;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* 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 <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);
uint32_t queue_family_index() const { return queue_family_index_; }
VkQueue primary_queue() const { return primary_queue_; }
const DeviceInfo& device_info() const { return device_info_; }
// 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_;
DeviceInfo device_info_;
uint32_t queue_family_index_ = 0;
VkQueue primary_queue_ = nullptr;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_DEVICE_H_

View File

@@ -0,0 +1,734 @@
/**
******************************************************************************
* 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_immediate_drawer.h"
#include "xenia/base/assert.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 {
#include "xenia/ui/vulkan/shaders/immediate.frag.h"
#include "xenia/ui/vulkan/shaders/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 err =
vkCreateBuffer(device_, &index_buffer_info, nullptr, &index_buffer_);
CheckResult(err, "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;
err =
vkCreateBuffer(*device, &vertex_buffer_info, nullptr, &vertex_buffer_);
CheckResult(err, "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.
err = vkMapMemory(device_, buffer_memory_, 0, VK_WHOLE_SIZE, 0,
&buffer_data_);
CheckResult(err, "vkMapMemory");
}
~LightweightCircularBuffer() {
vkUnmapMemory(device_, buffer_memory_);
vkDestroyBuffer(device_, index_buffer_, nullptr);
vkDestroyBuffer(device_, vertex_buffer_, nullptr);
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,
VkDescriptorSetLayout descriptor_set_layout,
VkSampler sampler, uint32_t width, uint32_t height)
: ImmediateTexture(width, height),
device_(*device),
descriptor_pool_(descriptor_pool),
sampler_(sampler) {
handle = reinterpret_cast<uintptr_t>(this);
// 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_SHADER_READ_ONLY_OPTIMAL;
auto err = vkCreateImage(device_, &image_info, nullptr, &image_);
CheckResult(err, "vkCreateImage");
// 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);
// Bind memory and the image together.
err = vkBindImageMemory(device_, image_, device_memory_, 0);
CheckResult(err, "vkBindImageMemory");
// 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};
err = vkCreateImageView(device_, &view_info, nullptr, &image_view_);
CheckResult(err, "vkCreateImageView");
// 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;
err = vkAllocateDescriptorSets(device_, &set_alloc_info, &descriptor_set_);
CheckResult(err, "vkAllocateDescriptorSets");
// 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);
}
~VulkanImmediateTexture() override {
vkFreeDescriptorSets(device_, descriptor_pool_, 1, &descriptor_set_);
vkDestroyImageView(device_, image_view_, nullptr);
vkDestroyImage(device_, image_, nullptr);
vkFreeMemory(device_, device_memory_, nullptr);
}
void 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.
void* gpu_data = nullptr;
auto err =
vkMapMemory(device_, device_memory_, 0, layout.size, 0, &gpu_data);
CheckResult(err, "vkMapMemory");
// Copy the entire texture, hoping its layout matches what we expect.
std::memcpy(gpu_data, src_data, layout.size);
vkUnmapMemory(device_, device_memory_);
}
VkDescriptorSet descriptor_set() const { return descriptor_set_; }
private:
VkDevice device_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkSampler sampler_ = nullptr; // Not owned.
VkImage image_ = nullptr;
VkImageLayout image_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
VkDeviceMemory device_memory_ = nullptr;
VkImageView image_view_ = nullptr;
VkDescriptorSet descriptor_set_ = nullptr;
};
VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
: ImmediateDrawer(graphics_context), context_(graphics_context) {
auto device = context_->device();
// NEAREST + CLAMP
VkSamplerCreateInfo sampler_info;
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.pNext = nullptr;
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;
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 err = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_clamp);
CheckResult(err, "vkCreateSampler");
// 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;
err = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.nearest_repeat);
CheckResult(err, "vkCreateSampler");
// 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;
err =
vkCreateSampler(*device, &sampler_info, nullptr, &samplers_.linear_clamp);
CheckResult(err, "vkCreateSampler");
// 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;
err = vkCreateSampler(*device, &sampler_info, nullptr,
&samplers_.linear_repeat);
CheckResult(err, "vkCreateSampler");
// 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;
err = vkCreateDescriptorSetLayout(*device, &texture_set_layout_info, nullptr,
&texture_set_layout_);
CheckResult(err, "vkCreateDescriptorSetLayout");
// 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;
err = vkCreateDescriptorPool(*device, &descriptor_pool_info, nullptr,
&descriptor_pool_);
CheckResult(err, "vkCreateDescriptorPool");
// 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;
err = vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr,
&pipeline_layout_);
CheckResult(err, "vkCreatePipelineLayout");
// 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_spv);
vertex_shader_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_vert_spv);
VkShaderModule vertex_shader;
err = vkCreateShaderModule(*device, &vertex_shader_info, nullptr,
&vertex_shader);
CheckResult(err, "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_spv);
fragment_shader_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_frag_spv);
VkShaderModule fragment_shader;
err = vkCreateShaderModule(*device, &fragment_shader_info, nullptr,
&fragment_shader);
CheckResult(err, "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;
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_BACK_BIT;
rasterization_info.frontFace = VK_FRONT_FACE_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 = 0;
err = vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info, nullptr,
&triangle_pipeline_);
CheckResult(err, "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 = 0;
err = vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info, nullptr,
&line_pipeline_);
CheckResult(err, "vkCreateGraphicsPipelines");
vkDestroyShaderModule(*device, vertex_shader, nullptr);
vkDestroyShaderModule(*device, fragment_shader, nullptr);
// Allocate the buffer we'll use for our vertex and index data.
circular_buffer_ = std::make_unique<LightweightCircularBuffer>(device);
}
VulkanImmediateDrawer::~VulkanImmediateDrawer() {
auto device = context_->device();
circular_buffer_.reset();
vkDestroyPipeline(*device, line_pipeline_, nullptr);
vkDestroyPipeline(*device, triangle_pipeline_, nullptr);
vkDestroyPipelineLayout(*device, pipeline_layout_, nullptr);
vkDestroyDescriptorPool(*device, descriptor_pool_, nullptr);
vkDestroyDescriptorSetLayout(*device, texture_set_layout_, nullptr);
vkDestroySampler(*device, samplers_.nearest_clamp, nullptr);
vkDestroySampler(*device, samplers_.nearest_repeat, nullptr);
vkDestroySampler(*device, samplers_.linear_clamp, nullptr);
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();
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;
}
auto texture = std::make_unique<VulkanImmediateTexture>(
device, descriptor_pool_, texture_set_layout_, sampler, width, height);
if (data) {
UpdateTexture(texture.get(), data);
}
return std::unique_ptr<ImmediateTexture>(texture.release());
}
void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
const uint8_t* data) {
static_cast<VulkanImmediateTexture*>(texture)->Upload(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();
// 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);
// 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) {
auto swap_chain = context_->swap_chain();
if (draw.primitive_type != ImmediatePrimitiveType::kTriangles) {
return;
}
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.
VkDescriptorSet texture_set = nullptr;
auto texture = reinterpret_cast<VulkanImmediateTexture*>(draw.texture_handle);
if (texture) {
texture_set = texture->descriptor_set();
}
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 = swap_chain->surface_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 = swap_chain->surface_width();
scissor.extent.height = swap_chain->surface_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::EndDrawBatch() {}
void VulkanImmediateDrawer::End() { current_cmd_buffer_ = nullptr; }
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,69 @@
/**
******************************************************************************
* 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_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;
std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width,
uint32_t height,
ImmediateTextureFilter filter,
bool repeat,
const uint8_t* data) override;
void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override;
void Begin(int render_target_width, int render_target_height) override;
void BeginDrawBatch(const ImmediateDrawBatch& batch) override;
void Draw(const ImmediateDraw& draw) override;
void EndDrawBatch() override;
void End() override;
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;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_IMMEDIATE_DRAWER_H_

View File

@@ -0,0 +1,486 @@
/**
******************************************************************************
* 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_instance.h"
#include <gflags/gflags.h>
#include <cinttypes>
#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_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_util.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
namespace vulkan {
VulkanInstance::VulkanInstance() {
if (FLAGS_vulkan_validation) {
// DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects", Version::Make(0,
// 0, 0), true);
DeclareRequiredLayer("VK_LAYER_LUNARG_threading", Version::Make(0, 0, 0),
true);
// DeclareRequiredLayer("VK_LAYER_LUNARG_mem_tracker", 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_param_checker",
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);
}
}
VulkanInstance::~VulkanInstance() { DestroyInstance(); }
bool VulkanInstance::Initialize(Window* any_target_window) {
auto version = Version::Parse(VK_API_VERSION);
XELOGVK("Initializing Vulkan %s...", version.pretty_string.c_str());
// 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(any_target_window)) {
XELOGE("Failed to query devices");
return false;
}
XELOGVK("Instance initialized successfully!");
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 %d 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("- %s (spec: %s, impl: %s)", global_layer.properties.layerName,
spec_version.pretty_string.c_str(),
impl_version.pretty_string.c_str());
XELOGVK(" %s", global_layer.properties.description);
if (!global_layer.extensions.empty()) {
XELOGVK(" %d 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 %d 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);
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: %s", to_string(err));
return false;
}
// 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) {
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("[%s/%s:%d] %s", 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 err = vk_create_debug_report_callback_ext(handle, &create_info, nullptr,
&dbg_report_callback_);
CheckResult(err, "vkCreateDebugReportCallbackEXT");
XELOGVK("Debug validation layer enabled");
}
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(Window* any_target_window) {
// 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 queue family presentation support.
// TODO(benvanik): move to swap chain?
VkSurfaceKHR any_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>(any_target_window->native_platform_handle());
create_info.hwnd = static_cast<HWND>(any_target_window->native_handle());
err = vkCreateWin32SurfaceKHR(handle, &create_info, nullptr, &any_surface);
CheckResult(err, "vkCreateWin32SurfaceKHR");
#else
#error Platform not yet implemented.
#endif // XE_PLATFORM_WIN32
device_info.queue_family_supports_present.resize(
device_info.queue_family_properties.size());
for (size_t j = 0; j < device_info.queue_family_supports_present.size();
++j) {
err = vkGetPhysicalDeviceSurfaceSupportKHR(
device_handle, static_cast<uint32_t>(j), any_surface,
&device_info.queue_family_supports_present[j]);
CheckResult(err, "vkGetPhysicalDeviceSurfaceSupportKHR");
}
vkDestroySurfaceKHR(handle, any_surface, nullptr);
// 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 %d physical devices:", available_devices_.size());
for (size_t i = 0; i < available_devices_.size(); ++i) {
auto& device_info = available_devices_[i];
XELOGVK("- Device %d:", 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("%s- %s (spec: %s, impl: %s)", indent, layer.properties.layerName,
spec_version.pretty_string.c_str(),
impl_version.pretty_string.c_str());
XELOGVK("%s %s", indent, layer.properties.description);
if (!layer.extensions.empty()) {
XELOGVK("%s %d 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("%s- %s (%s)", indent, extension.extensionName,
version.pretty_string.c_str());
}
}
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 = %s", api_version.pretty_string.c_str());
XELOGVK(" driverVersion = %s", driver_version.pretty_string.c_str());
XELOGVK(" vendorId = 0x%04x", properties.vendorID);
XELOGVK(" deviceId = 0x%04x", properties.deviceID);
XELOGVK(" deviceType = %s", to_string(properties.deviceType));
XELOGVK(" deviceName = %s", properties.deviceName);
auto& memory_props = device_info.memory_properties;
XELOGVK(" Memory Heaps:");
for (size_t j = 0; j < memory_props.memoryHeapCount; ++j) {
XELOGVK(" - Heap %u: %" PRIu64 " 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 %u:", 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 %d:", j);
XELOGVK(
" queueFlags = %s, %s, %s, %s",
(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 = %u", queue_props.queueCount);
XELOGVK(" timestampValidBits = %u", queue_props.timestampValidBits);
XELOGVK(" supportsPresent = %s",
device_info.queue_family_supports_present[j] ? "true" : "false");
}
XELOGVK(" Layers:");
DumpLayers(device_info.layers, " ");
XELOGVK(" Extensions:");
DumpExtensions(device_info.extensions, " ");
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,95 @@
/**
******************************************************************************
* 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.
// TODO(benvanik): remove need for any_target_window - it's just for queries.
bool Initialize(Window* any_target_window);
// Returns a list of all available devices as detected during initialization.
const std::vector<DeviceInfo>& available_devices() const {
return available_devices_;
}
private:
// 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(Window* any_target_window);
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;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_INSTANCE_H_

View File

@@ -0,0 +1,107 @@
/**
******************************************************************************
* 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_provider.h"
#include <gflags/gflags.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.");
namespace xe {
namespace ui {
namespace vulkan {
std::unique_ptr<GraphicsProvider> 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"
"Ensure you have the latest drivers for your GPU and that it "
"supports Vulkan. See http://xenia.jp/faq/ for more information and a "
"list of supported GPUs.");
return nullptr;
}
return std::unique_ptr<GraphicsProvider>(provider.release());
}
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.
instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0),
false);
instance_->DeclareRequiredExtension("VK_KHR_win32_surface",
Version::Make(0, 0, 0), false);
// Attempt initialization and device query.
if (!instance_->Initialize(main_window_)) {
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(), FLAGS_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;
}
std::unique_ptr<GraphicsContext> VulkanProvider::CreateContext(
Window* target_window) {
auto new_context =
std::unique_ptr<VulkanContext>(new VulkanContext(this, target_window));
if (!new_context->Initialize()) {
return nullptr;
}
return std::unique_ptr<GraphicsContext>(new_context.release());
}
std::unique_ptr<GraphicsContext> VulkanProvider::CreateOffscreenContext() {
auto new_context =
std::unique_ptr<VulkanContext>(new VulkanContext(this, nullptr));
if (!new_context->Initialize()) {
return nullptr;
}
return std::unique_ptr<GraphicsContext>(new_context.release());
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,50 @@
/**
******************************************************************************
* 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_PROVIDER_H_
#define XENIA_UI_VULKAN_VULKAN_PROVIDER_H_
#include <memory>
#include "xenia/ui/graphics_provider.h"
namespace xe {
namespace ui {
namespace vulkan {
class VulkanDevice;
class VulkanInstance;
class VulkanProvider : public GraphicsProvider {
public:
~VulkanProvider() override;
static std::unique_ptr<GraphicsProvider> 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:
explicit VulkanProvider(Window* main_window);
bool Initialize();
std::unique_ptr<VulkanInstance> instance_;
std::unique_ptr<VulkanDevice> device_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_PROVIDER_H_

View File

@@ -0,0 +1,510 @@
/**
******************************************************************************
* 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 <gflags/gflags.h>
#include <mutex>
#include <string>
#include "xenia/base/assert.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.");
namespace xe {
namespace ui {
namespace vulkan {
VulkanSwapChain::VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device)
: instance_(instance), device_(device) {}
VulkanSwapChain::~VulkanSwapChain() {
for (auto& buffer : buffers_) {
DestroyBuffer(&buffer);
}
if (image_available_semaphore_) {
vkDestroySemaphore(*device_, image_available_semaphore_, nullptr);
}
if (render_pass_) {
vkDestroyRenderPass(*device_, render_pass_, nullptr);
}
if (render_cmd_buffer_) {
vkFreeCommandBuffers(*device_, cmd_pool_, 1, &render_cmd_buffer_);
}
if (cmd_pool_) {
vkDestroyCommandPool(*device_, cmd_pool_, nullptr);
}
// images_ doesn't need to be cleaned up as the swapchain does it implicitly.
if (handle) {
vkDestroySwapchainKHR(*device_, handle, nullptr);
handle = nullptr;
}
if (surface_) {
vkDestroySurfaceKHR(*instance_, surface_, nullptr);
}
}
bool VulkanSwapChain::Initialize(VkSurfaceKHR surface) {
surface_ = surface;
// Query supported target formats.
uint32_t count = 0;
auto err =
vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, nullptr);
CheckResult(err, "vkGetPhysicalDeviceSurfaceFormatsKHR");
std::vector<VkSurfaceFormatKHR> surface_formats;
surface_formats.resize(count);
err = vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count,
surface_formats.data());
CheckResult(err, "vkGetPhysicalDeviceSurfaceFormatsKHR");
// 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;
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_,
&surface_caps);
CheckResult(err, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
// Query surface properties so we can configure ourselves within bounds.
std::vector<VkPresentModeKHR> present_modes;
err = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
nullptr);
CheckResult(err, "vkGetPhysicalDeviceSurfacePresentModesKHR");
present_modes.resize(count);
err = vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, &count,
present_modes.data());
CheckResult(err, "vkGetPhysicalDeviceSurfacePresentModesKHR");
// 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 (%d) exceeds maximum (%d)",
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;
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 = %u", create_info.minImageCount);
XELOGVK(" imageFormat = %s", to_string(create_info.imageFormat));
XELOGVK(" imageExtent = %d x %d", create_info.imageExtent.width,
create_info.imageExtent.height);
auto pre_transform_str = to_flags_string(create_info.preTransform);
XELOGVK(" preTransform = %s", pre_transform_str.c_str());
XELOGVK(" imageArrayLayers = %u", create_info.imageArrayLayers);
XELOGVK(" presentMode = %s", to_string(create_info.presentMode));
XELOGVK(" clipped = %s", create_info.clipped ? "true" : "false");
XELOGVK(" imageColorSpace = %s", to_string(create_info.imageColorSpace));
auto image_usage_flags_str = to_flags_string(create_info.imageUsage);
XELOGVK(" imageUsageFlags = %s", image_usage_flags_str.c_str());
XELOGVK(" imageSharingMode = %s", to_string(create_info.imageSharingMode));
XELOGVK(" queueFamilyCount = %u", create_info.queueFamilyIndexCount);
err = vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle);
if (err) {
XELOGE("Failed to create swapchain: %s", to_string(err));
return false;
}
// 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 = device_->queue_family_index();
err = vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_);
CheckResult(err, "vkCreateCommandPool");
// Make a command buffer we'll do all our primary rendering from.
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 = 1;
err =
vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &render_cmd_buffer_);
CheckResult(err, "vkCreateCommandBuffer");
// 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_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.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;
err = vkCreateRenderPass(*device_, &render_pass_info, nullptr, &render_pass_);
CheckResult(err, "vkCreateRenderPass");
// 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;
err = vkCreateSemaphore(*device_, &semaphore_info, nullptr,
&image_available_semaphore_);
CheckResult(err, "vkCreateSemaphore");
// 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;
err = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, nullptr);
CheckResult(err, "vkGetSwapchainImagesKHR");
images.resize(actual_image_count);
err = vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count,
images.data());
CheckResult(err, "vkGetSwapchainImagesKHR");
// Create all buffers.
buffers_.resize(images.size());
for (size_t i = 0; i < buffers_.size(); ++i) {
if (!InitializeBuffer(&buffers_[i], images[i])) {
XELOGE("Failed to initialize a swapchain buffer");
return false;
}
}
XELOGVK("Swap chain initialized successfully!");
return true;
}
bool VulkanSwapChain::InitializeBuffer(Buffer* buffer, VkImage target_image) {
DestroyBuffer(buffer);
buffer->image = target_image;
// 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;
auto err = vkCreateImageView(*device_, &image_view_info, nullptr,
&buffer->image_view);
CheckResult(err, "vkCreateImageView");
// 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.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;
err = vkCreateFramebuffer(*device_, &framebuffer_info, nullptr,
&buffer->framebuffer);
CheckResult(err, "vkCreateFramebuffer");
return true;
}
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;
}
bool VulkanSwapChain::Begin() {
// Get the index of the next available swapchain image.
auto err =
vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_,
nullptr, &current_buffer_index_);
CheckResult(err, "vkAcquireNextImageKHR");
// 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;
wait_submit_info.waitSemaphoreCount = 1;
wait_submit_info.pWaitSemaphores = &image_available_semaphore_;
wait_submit_info.commandBufferCount = 0;
wait_submit_info.pCommandBuffers = nullptr;
wait_submit_info.signalSemaphoreCount = 0;
wait_submit_info.pSignalSemaphores = nullptr;
err = vkQueueSubmit(device_->primary_queue(), 1, &wait_submit_info, nullptr);
CheckResult(err, "vkQueueSubmit");
// Reset all command buffers.
vkResetCommandBuffer(render_cmd_buffer_,
VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
auto& current_buffer = buffers_[current_buffer_index_];
// Build the command buffer that will execute all queued rendering buffers.
VkCommandBufferBeginInfo begin_info;
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.pNext = nullptr;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
begin_info.pInheritanceInfo = nullptr;
err = vkBeginCommandBuffer(render_cmd_buffer_, &begin_info);
CheckResult(err, "vkBeginCommandBuffer");
// Transition the image to a format we can render to.
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.srcAccessMask = 0;
pre_image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
pre_image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
pre_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
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;
vkCmdPipelineBarrier(render_cmd_buffer_, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &pre_image_memory_barrier);
// Begin render pass.
VkClearValue color_clear_value;
color_clear_value.color.float32[0] = 238 / 255.0f;
color_clear_value.color.float32[1] = 238 / 255.0f;
color_clear_value.color.float32[2] = 238 / 255.0f;
color_clear_value.color.float32[3] = 1.0f;
if (FLAGS_vulkan_random_clear_color) {
color_clear_value.color.float32[0] =
rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
color_clear_value.color.float32[1] = 1.0f;
color_clear_value.color.float32[2] = 0.0f;
}
VkClearValue clear_values[] = {color_clear_value};
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 =
static_cast<uint32_t>(xe::countof(clear_values));
render_pass_begin_info.pClearValues = clear_values;
vkCmdBeginRenderPass(render_cmd_buffer_, &render_pass_begin_info,
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
return true;
}
bool VulkanSwapChain::End() {
auto& current_buffer = buffers_[current_buffer_index_];
// End render pass.
vkCmdEndRenderPass(render_cmd_buffer_);
// Transition the image to a format the presentation engine can source from.
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 = 0;
post_image_memory_barrier.oldLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
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(render_cmd_buffer_, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &post_image_memory_barrier);
auto err = vkEndCommandBuffer(render_cmd_buffer_);
CheckResult(err, "vkEndCommandBuffer");
// Submit rendering.
VkSubmitInfo render_submit_info;
render_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
render_submit_info.pNext = nullptr;
render_submit_info.waitSemaphoreCount = 0;
render_submit_info.pWaitSemaphores = nullptr;
render_submit_info.commandBufferCount = 1;
render_submit_info.pCommandBuffers = &render_cmd_buffer_;
render_submit_info.signalSemaphoreCount = 0;
render_submit_info.pSignalSemaphores = nullptr;
err =
vkQueueSubmit(device_->primary_queue(), 1, &render_submit_info, nullptr);
CheckResult(err, "vkQueueSubmit");
// 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;
err = vkQueuePresentKHR(device_->primary_queue(), &present_info);
switch (err) {
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.
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;
default:
XELOGE("Failed to queue present: %s", to_string(err));
assert_always("Unexpected queue present failure");
return false;
}
return true;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,80 @@
/**
******************************************************************************
* 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 <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_; }
// 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_; }
bool Initialize(VkSurfaceKHR surface);
// Begins the swap operation, preparing state for rendering.
bool Begin();
// Ends the swap operation, finalizing rendering and presenting the results.
bool End();
private:
struct Buffer {
VkImage image = nullptr;
VkImageView image_view = nullptr;
VkFramebuffer framebuffer = nullptr;
};
bool InitializeBuffer(Buffer* buffer, VkImage target_image);
void DestroyBuffer(Buffer* buffer);
VulkanInstance* instance_ = nullptr;
VulkanDevice* device_ = nullptr;
VkSurfaceKHR surface_ = nullptr;
uint32_t surface_width_ = 0;
uint32_t surface_height_ = 0;
VkFormat surface_format_ = VK_FORMAT_UNDEFINED;
VkCommandPool cmd_pool_ = nullptr;
VkCommandBuffer render_cmd_buffer_ = nullptr;
VkRenderPass render_pass_ = nullptr;
VkSemaphore image_available_semaphore_ = nullptr;
uint32_t current_buffer_index_ = 0;
std::vector<Buffer> buffers_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_

View File

@@ -0,0 +1,464 @@
/**
******************************************************************************
* 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_util.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.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 = xe::format_string("%u.%u.%u", 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);
#undef STR
default:
return "UNKNOWN_RESULT";
}
}
std::string to_flags_string(VkImageUsageFlags 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(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 +
"\nEnsure you have the latest drivers for your GPU and that it supports "
"Vulkan. See http://xenia.jp/faq/ for more information and a list"
"of supported GPUs.");
}
void CheckResult(VkResult result, const char* action) {
if (result) {
XELOGE("Vulkan check: %s returned %s", 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 %s (%s) version mismatch", layer_name,
layer_version.pretty_string.c_str());
continue;
}
XELOGE("ERROR: required layer %s (%s) version mismatch", layer_name,
layer_version.pretty_string.c_str());
any_missing = true;
break;
}
XELOGVK("- enabling layer %s (%s)", layer_name,
layer_version.pretty_string.c_str());
enabled_layers.push_back(layer_name);
break;
}
}
if (!found) {
if (requirement.is_optional) {
XELOGVK("- optional layer %s not found", requirement.name.c_str());
} else {
XELOGE("ERROR: required layer %s not found", requirement.name.c_str());
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 %s (%s) version mismatch",
extension_name, extension_version.pretty_string.c_str());
continue;
}
XELOGE("ERROR: required extension %s (%s) version mismatch",
extension_name, extension_version.pretty_string.c_str());
any_missing = true;
break;
}
XELOGVK("- enabling extension %s (%s)", extension_name,
extension_version.pretty_string.c_str());
enabled_extensions.push_back(extension_name);
break;
}
}
if (!found) {
if (requirement.is_optional) {
XELOGVK("- optional extension %s not found", requirement.name.c_str());
} else {
XELOGE("ERROR: required extension %s not found",
requirement.name.c_str());
any_missing = true;
}
}
}
return {!any_missing, enabled_extensions};
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@@ -0,0 +1,101 @@
/**
******************************************************************************
* 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 "xenia/ui/vulkan/vulkan.h"
namespace xe {
namespace ui {
class Window;
} // namespace ui
} // namespace xe
namespace xe {
namespace ui {
namespace vulkan {
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(VkImageUsageFlags 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<VkBool32> queue_family_supports_present;
std::vector<LayerInfo> layers;
std::vector<VkExtensionProperties> extensions;
};
// Defines a requirement for a layer or extension, used to both verify and
// enable them on initialization.
struct Requirement {
// Layer or extension name.
std::string name;
// Minimum required spec version of the layer or extension.
uint32_t min_version;
// True if the requirement is optional (will not cause verification to fail).
bool is_optional;
};
// Gets a list of enabled layer names based on the given layer requirements and
// available layer info.
// Returns a boolean indicating whether all required layers are present.
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<LayerInfo>& layer_infos);
// Gets a list of enabled extension names based on the given extension
// requirements and available extensions.
// Returns a boolean indicating whether all required extensions are present.
std::pair<bool, std::vector<const char*>> CheckRequirements(
const std::vector<Requirement>& requirements,
const std::vector<VkExtensionProperties>& extension_properties);
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_VULKAN_UTIL_H_

View File

@@ -0,0 +1,30 @@
/**
******************************************************************************
* 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 <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::wstring>& args);
std::unique_ptr<GraphicsProvider> CreateDemoGraphicsProvider(Window* window) {
return xe::ui::vulkan::VulkanProvider::Create(window);
}
} // namespace ui
} // namespace xe
DEFINE_ENTRY_POINT(L"xenia-ui-window-vulkan-demo",
L"xenia-ui-window-vulkan-demo", xe::ui::window_demo_main);

View File

@@ -87,11 +87,10 @@ int window_demo_main(const std::vector<std::wstring>& args) {
});
window->on_closed.AddListener(
[&loop, &graphics_provider](xe::ui::UIEvent* e) {
[&loop, &window, &graphics_provider](xe::ui::UIEvent* e) {
loop->Quit();
Profiler::Shutdown();
XELOGI("User-initiated death!");
graphics_provider.reset();
exit(1);
});
loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); });
@@ -116,11 +115,9 @@ int window_demo_main(const std::vector<std::wstring>& args) {
// Wait until we are exited.
loop->AwaitQuit();
loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); });
window.reset();
loop.reset();
Profiler::Dump();
Profiler::Shutdown();
graphics_provider.reset();
return 0;
}