Merge branch 'master' into vulkan

This commit is contained in:
Triang3l
2022-02-13 23:25:39 +03:00
40 changed files with 368 additions and 16034 deletions

View File

@@ -2,18 +2,13 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/surface_win.h"
#include <algorithm>
#include <memory>
#include "xenia/base/logging.h"
namespace xe {
namespace ui {
@@ -24,9 +19,9 @@ bool Win32HwndSurface::GetSizeImpl(uint32_t& width_out,
if (!GetClientRect(hwnd(), &client_rect)) {
return false;
}
width_out = uint32_t(std::max(client_rect.right - client_rect.left, LONG(0)));
height_out =
uint32_t(std::max(client_rect.bottom - client_rect.top, LONG(0)));
// GetClientRect returns a rectangle with 0 origin.
width_out = uint32_t(client_rect.right);
height_out = uint32_t(client_rect.bottom);
return true;
}
#endif

View File

@@ -10,6 +10,9 @@ project("xenia-ui-vulkan")
"xenia-base",
"xenia-ui",
})
includedirs({
project_root.."/third_party/Vulkan-Headers/include",
})
local_platform_files()
local_platform_files("functions")
files({
@@ -28,6 +31,9 @@ project("xenia-ui-window-vulkan-demo")
"xenia-ui",
"xenia-ui-vulkan",
})
includedirs({
project_root.."/third_party/Vulkan-Headers/include",
})
files({
"../window_demo.cc",
"vulkan_window_demo.cc",

View File

@@ -10,7 +10,9 @@
#include "xenia/ui/vulkan/vulkan_provider.h"
#include <cfloat>
#include <cstddef>
#include <cstring>
#include <utility>
#include <vector>
#include "xenia/base/assert.h"
@@ -706,47 +708,64 @@ bool VulkanProvider::Initialize() {
}
}
device_extensions_enabled.clear();
// Checking if already enabled as an optimization to do fewer and fewer
// string comparisons, as well as to skip adding extensions promoted to the
// core to device_extensions_enabled. Adding literals to
// device_extensions_enabled for the most C string lifetime safety.
static const std::pair<const char*, size_t> kUsedDeviceExtensions[] = {
{"VK_EXT_fragment_shader_interlock",
offsetof(DeviceExtensions, ext_fragment_shader_interlock)},
{"VK_KHR_dedicated_allocation",
offsetof(DeviceExtensions, khr_dedicated_allocation)},
{"VK_KHR_image_format_list",
offsetof(DeviceExtensions, khr_image_format_list)},
{"VK_KHR_portability_subset",
offsetof(DeviceExtensions, khr_portability_subset)},
{"VK_KHR_shader_float_controls",
offsetof(DeviceExtensions, khr_shader_float_controls)},
{"VK_KHR_spirv_1_4", offsetof(DeviceExtensions, khr_spirv_1_4)},
{"VK_KHR_swapchain", offsetof(DeviceExtensions, khr_swapchain)},
};
for (const VkExtensionProperties& device_extension :
device_extension_properties) {
const char* device_extension_name = device_extension.extensionName;
// Checking if already enabled as an optimization to do fewer and fewer
// string comparisons, as well as to skip adding extensions promoted to
// the core to device_extensions_enabled. Adding literals to
// device_extensions_enabled for the most C string lifetime safety.
if (!device_extensions_.ext_fragment_shader_interlock &&
!std::strcmp(device_extension_name,
"VK_EXT_fragment_shader_interlock")) {
device_extensions_enabled.push_back("VK_EXT_fragment_shader_interlock");
device_extensions_.ext_fragment_shader_interlock = true;
} else if (!device_extensions_.khr_dedicated_allocation &&
!std::strcmp(device_extension_name,
"VK_KHR_dedicated_allocation")) {
device_extensions_enabled.push_back("VK_KHR_dedicated_allocation");
device_extensions_.khr_dedicated_allocation = true;
} else if (!device_extensions_.khr_image_format_list &&
!std::strcmp(device_extension_name,
"VK_KHR_image_format_list")) {
device_extensions_enabled.push_back("VK_KHR_image_format_list");
device_extensions_.khr_image_format_list = true;
} else if (!device_extensions_.khr_shader_float_controls &&
!std::strcmp(device_extension_name,
"VK_KHR_shader_float_controls")) {
device_extensions_enabled.push_back("VK_KHR_shader_float_controls");
device_extensions_.khr_shader_float_controls = true;
} else if (!device_extensions_.khr_spirv_1_4 &&
!std::strcmp(device_extension_name, "VK_KHR_spirv_1_4")) {
device_extensions_enabled.push_back("VK_KHR_spirv_1_4");
device_extensions_.khr_spirv_1_4 = true;
} else if (!device_extensions_.khr_swapchain &&
!std::strcmp(device_extension_name, "VK_KHR_swapchain")) {
device_extensions_enabled.push_back("VK_KHR_swapchain");
device_extensions_.khr_swapchain = true;
for (const std::pair<const char*, size_t>& used_device_extension :
kUsedDeviceExtensions) {
bool& device_extension_flag = *reinterpret_cast<bool*>(
reinterpret_cast<char*>(&device_extensions_) +
used_device_extension.second);
if (!device_extension_flag &&
!std::strcmp(device_extension.extensionName,
used_device_extension.first)) {
device_extensions_enabled.push_back(used_device_extension.first);
device_extension_flag = true;
}
}
}
if (is_surface_required_ && !device_extensions_.khr_swapchain) {
continue;
}
// Get portability subset features.
// VK_KHR_portability_subset reduces, not increases, the capabilities, skip
// the device completely if there's no way to retrieve what is actually
// unsupported. Though VK_KHR_portability_subset requires
// VK_KHR_get_physical_device_properties2, check just in case of an
// untrustworthy driver.
if (device_extensions_.khr_portability_subset) {
if (!instance_extensions_.khr_get_physical_device_properties2) {
continue;
}
device_portability_subset_features_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR;
device_portability_subset_features_.pNext = nullptr;
VkPhysicalDeviceProperties2KHR device_properties_2;
device_properties_2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
device_properties_2.pNext = &device_portability_subset_features_;
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
&device_properties_2);
}
// Get the memory types.
VkPhysicalDeviceMemoryProperties memory_properties;
ifn_.vkGetPhysicalDeviceMemoryProperties(physical_device_current,
@@ -843,6 +862,7 @@ bool VulkanProvider::Initialize() {
VkDeviceCreateInfo device_create_info;
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.pNext = nullptr;
VkDeviceCreateInfo* device_create_info_last = &device_create_info;
device_create_info.flags = 0;
device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size());
device_create_info.pQueueCreateInfos = queue_create_infos.data();
@@ -854,6 +874,13 @@ bool VulkanProvider::Initialize() {
device_create_info.ppEnabledExtensionNames = device_extensions_enabled.data();
// TODO(Triang3l): Enable only needed features.
device_create_info.pEnabledFeatures = &device_features_;
if (device_extensions_.khr_portability_subset) {
// TODO(Triang3l): Enable only needed portability subset features.
device_portability_subset_features_.pNext = nullptr;
device_create_info_last->pNext = &device_portability_subset_features_;
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
&device_portability_subset_features_);
}
if (ifn_.vkCreateDevice(physical_device_, &device_create_info, nullptr,
&device_) != VK_SUCCESS) {
XELOGE("Failed to create a Vulkan device");
@@ -923,6 +950,34 @@ bool VulkanProvider::Initialize() {
device_extensions_.khr_dedicated_allocation ? "yes" : "no");
XELOGVK("* VK_KHR_image_format_list: {}",
device_extensions_.khr_image_format_list ? "yes" : "no");
XELOGVK("* VK_KHR_portability_subset: {}",
device_extensions_.khr_portability_subset ? "yes" : "no");
if (device_extensions_.khr_portability_subset) {
XELOGVK(" * Constant alpha color blend factors: {}",
device_portability_subset_features_.constantAlphaColorBlendFactors
? "yes"
: "no");
XELOGVK(" * Image view format reinterpretation: {}",
device_portability_subset_features_.imageViewFormatReinterpretation
? "yes"
: "no");
XELOGVK(" * Image view format swizzle: {}",
device_portability_subset_features_.imageViewFormatSwizzle ? "yes"
: "no");
XELOGVK(" * Point polygons: {}",
device_portability_subset_features_.pointPolygons ? "yes" : "no");
XELOGVK(
" * Separate stencil front and back masks and reference values: {}",
device_portability_subset_features_.separateStencilMaskRef ? "yes"
: "no");
XELOGVK(" * Shader sample rate interpolation functions: {}",
device_portability_subset_features_
.shaderSampleRateInterpolationFunctions
? "yes"
: "no");
XELOGVK(" * Triangle fans: {}",
device_portability_subset_features_.triangleFans ? "yes" : "no");
}
XELOGVK("* VK_KHR_shader_float_controls: {}",
device_extensions_.khr_shader_float_controls ? "yes" : "no");
if (device_extensions_.khr_shader_float_controls) {

View File

@@ -39,10 +39,13 @@
#endif
#endif
#ifndef VK_ENABLE_BETA_EXTENSIONS
#define VK_ENABLE_BETA_EXTENSIONS 1
#endif
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES 1
#endif
#include "third_party/vulkan/vulkan.h"
#include "third_party/Vulkan-Headers/include/vulkan/vulkan.h"
#define XELOGVK XELOGI
@@ -133,6 +136,8 @@ class VulkanProvider : public GraphicsProvider {
bool khr_dedicated_allocation;
// Core since 1.2.0.
bool khr_image_format_list;
// Requires the VK_KHR_get_physical_device_properties2 instance extension.
bool khr_portability_subset;
// Core since 1.2.0.
bool khr_shader_float_controls;
// Core since 1.2.0.
@@ -142,6 +147,14 @@ class VulkanProvider : public GraphicsProvider {
const DeviceExtensions& device_extensions() const {
return device_extensions_;
}
// Returns nullptr if the device is fully compliant with Vulkan 1.0.
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
device_portability_subset_features() const {
if (!device_extensions_.khr_portability_subset) {
return nullptr;
}
return &device_portability_subset_features_;
}
uint32_t memory_types_device_local() const {
return memory_types_device_local_;
}
@@ -269,6 +282,8 @@ class VulkanProvider : public GraphicsProvider {
VkPhysicalDeviceProperties device_properties_;
VkPhysicalDeviceFeatures device_features_;
DeviceExtensions device_extensions_;
VkPhysicalDevicePortabilitySubsetFeaturesKHR
device_portability_subset_features_;
uint32_t memory_types_device_local_;
uint32_t memory_types_host_visible_;
uint32_t memory_types_host_coherent_;

View File

@@ -9,6 +9,7 @@
#include "xenia/ui/window_win.h"
#include <algorithm>
#include <memory>
#include <string>
@@ -238,27 +239,32 @@ bool Win32Window::OpenImpl() {
shown_placement.length = sizeof(shown_placement);
if (GetWindowPlacement(hwnd_, &shown_placement)) {
// Get the size of the non-client area to subtract it from the size of the
// entire window in its non-maximized state, to get the client area.
// entire window in its non-maximized state, to get the client area. For
// safety, in case the window is somehow smaller than its non-client area
// (AdjustWindowRect is not exact in various cases also, such as when the
// menu becomes multiline), clamp to 0.
RECT non_client_area_rect = {};
AdjustWindowRectangle(non_client_area_rect);
OnDesiredLogicalSizeUpdate(
SizeToLogical(uint32_t(
SizeToLogical(uint32_t(std::max(
(shown_placement.rcNormalPosition.right -
shown_placement.rcNormalPosition.left) -
(non_client_area_rect.right - non_client_area_rect.left))),
SizeToLogical(uint32_t(
(non_client_area_rect.right - non_client_area_rect.left),
LONG(0)))),
SizeToLogical(uint32_t(std::max(
(shown_placement.rcNormalPosition.bottom -
shown_placement.rcNormalPosition.top) -
(non_client_area_rect.bottom - non_client_area_rect.top))));
(non_client_area_rect.bottom - non_client_area_rect.top),
LONG(0)))));
}
// Report the actual physical size in the current state.
// GetClientRect returns a rectangle with 0 origin.
RECT shown_client_rect;
if (GetClientRect(hwnd_, &shown_client_rect)) {
OnActualSizeUpdate(
uint32_t(shown_client_rect.right - shown_client_rect.left),
uint32_t(shown_client_rect.bottom - shown_client_rect.top),
destruction_receiver);
OnActualSizeUpdate(uint32_t(shown_client_rect.right),
uint32_t(shown_client_rect.bottom),
destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return true;
}
@@ -632,17 +638,22 @@ void Win32Window::ApplyFullscreenEntry(
// Preserve values for DPI rescaling of the window in the non-maximized state
// if DPI is changed mid-fullscreen.
// Get the size of the non-client area to subtract it from the size of the
// entire window in its non-maximized state, to get the client area.
// entire window in its non-maximized state, to get the client area. For
// safety, in case the window is somehow smaller than its non-client area
// (AdjustWindowRect is not exact in various cases also, such as when the menu
// becomes multiline), clamp to 0.
RECT non_client_area_rect = {};
AdjustWindowRectangle(non_client_area_rect);
pre_fullscreen_normal_client_width_ =
uint32_t((pre_fullscreen_placement_.rcNormalPosition.right -
pre_fullscreen_normal_client_width_ = uint32_t(
std::max((pre_fullscreen_placement_.rcNormalPosition.right -
pre_fullscreen_placement_.rcNormalPosition.left) -
(non_client_area_rect.right - non_client_area_rect.left));
pre_fullscreen_normal_client_height_ =
uint32_t((pre_fullscreen_placement_.rcNormalPosition.bottom -
(non_client_area_rect.right - non_client_area_rect.left),
LONG(0)));
pre_fullscreen_normal_client_height_ = uint32_t(
std::max((pre_fullscreen_placement_.rcNormalPosition.bottom -
pre_fullscreen_placement_.rcNormalPosition.top) -
(non_client_area_rect.bottom - non_client_area_rect.top));
(non_client_area_rect.bottom - non_client_area_rect.top),
LONG(0)));
// Changing the style and the menu may change the size too, don't handle the
// resize multiple times (also potentially with the listeners changing the
@@ -716,27 +727,32 @@ void Win32Window::HandleSizeUpdate(
// window_placement.rcNormalPosition is the entire window's rectangle, not
// only the client area - convert to client.
// https://devblogs.microsoft.com/oldnewthing/20131017-00/?p=2903
RECT non_client_rect = {};
if (AdjustWindowRectangle(non_client_rect)) {
// For safety, in case the window is somehow smaller than its non-client
// area (AdjustWindowRect is not exact in various cases also, such as when
// the menu becomes multiline), clamp to 0.
RECT non_client_area_rect = {};
if (AdjustWindowRectangle(non_client_area_rect)) {
OnDesiredLogicalSizeUpdate(
SizeToLogical(uint32_t((window_placement.rcNormalPosition.right -
non_client_rect.right) -
(window_placement.rcNormalPosition.left -
non_client_rect.left))),
SizeToLogical(uint32_t((window_placement.rcNormalPosition.bottom -
non_client_rect.bottom) -
(window_placement.rcNormalPosition.top -
non_client_rect.top))));
SizeToLogical(uint32_t(std::max(
(window_placement.rcNormalPosition.right -
window_placement.rcNormalPosition.left) -
(non_client_area_rect.right - non_client_area_rect.left),
LONG(0)))),
SizeToLogical(uint32_t(std::max(
(window_placement.rcNormalPosition.bottom -
window_placement.rcNormalPosition.top) -
(non_client_area_rect.bottom - non_client_area_rect.top),
LONG(0)))));
}
}
}
// For the actual state.
// GetClientRect returns a rectangle with 0 origin.
RECT client_rect;
if (GetClientRect(hwnd_, &client_rect)) {
OnActualSizeUpdate(uint32_t(client_rect.right - client_rect.left),
uint32_t(client_rect.bottom - client_rect.top),
destruction_receiver);
OnActualSizeUpdate(uint32_t(client_rect.right),
uint32_t(client_rect.bottom), destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return;
}

View File

@@ -22,6 +22,9 @@ Win32WindowedAppContext::~Win32WindowedAppContext() {
if (pending_functions_hwnd_) {
DestroyWindow(pending_functions_hwnd_);
}
if (user32_module_) {
FreeLibrary(user32_module_);
}
if (shcore_module_) {
FreeLibrary(shcore_module_);
}
@@ -38,25 +41,28 @@ bool Win32WindowedAppContext::Initialize() {
(*reinterpret_cast<void**>(
&per_monitor_dpi_v1_api_.get_dpi_for_monitor) =
GetProcAddress(shcore_module_, "GetDpiForMonitor")) != nullptr;
}
user32_module_ = LoadLibraryW(L"user32.dll");
if (user32_module_) {
per_monitor_dpi_v2_api_available_ = true;
per_monitor_dpi_v2_api_available_ &=
(*reinterpret_cast<void**>(
&per_monitor_dpi_v2_api_.adjust_window_rect_ex_for_dpi) =
GetProcAddress(shcore_module_, "AdjustWindowRectExForDpi")) !=
GetProcAddress(user32_module_, "AdjustWindowRectExForDpi")) !=
nullptr;
per_monitor_dpi_v2_api_available_ &=
(*reinterpret_cast<void**>(
&per_monitor_dpi_v2_api_.enable_non_client_dpi_scaling) =
GetProcAddress(shcore_module_, "EnableNonClientDpiScaling")) !=
GetProcAddress(user32_module_, "EnableNonClientDpiScaling")) !=
nullptr;
per_monitor_dpi_v2_api_available_ &=
(*reinterpret_cast<void**>(
&per_monitor_dpi_v2_api_.get_dpi_for_system) =
GetProcAddress(shcore_module_, "GetDpiForSystem")) != nullptr;
GetProcAddress(user32_module_, "GetDpiForSystem")) != nullptr;
per_monitor_dpi_v2_api_available_ &=
(*reinterpret_cast<void**>(
&per_monitor_dpi_v2_api_.get_dpi_for_window) =
GetProcAddress(shcore_module_, "GetDpiForWindow")) != nullptr;
GetProcAddress(user32_module_, "GetDpiForWindow")) != nullptr;
}
// Create the message-only window for executing pending functions - using a

View File

@@ -97,6 +97,7 @@ class Win32WindowedAppContext final : public WindowedAppContext {
int show_cmd_;
HMODULE shcore_module_ = nullptr;
HMODULE user32_module_ = nullptr;
PerMonitorDpiV1Api per_monitor_dpi_v1_api_ = {};
PerMonitorDpiV2Api per_monitor_dpi_v2_api_ = {};
bool per_monitor_dpi_v1_api_available_ = false;