Initial commit

This commit is contained in:
Philip Rebohle 2017-10-10 23:32:13 +02:00
commit 00e63d71a9
64 changed files with 43683 additions and 0 deletions

16
build-win64.txt Normal file
View File

@ -0,0 +1,16 @@
[binaries]
c = '/usr/bin/x86_64-w64-mingw32-gcc'
cpp = '/usr/bin/x86_64-w64-mingw32-g++'
ar = '/usr/bin/x86_64-w64-mingw32-ar'
strip = '/usr/bin/x86_64-w64-mingw32-strip'
exe_wrapper = 'wine'
[properties]
cpp_args = ['-std=c++17']
cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

152
include/vulkan/vk_icd.h Normal file
View File

@ -0,0 +1,152 @@
//
// File: vk_icd.h
//
/*
* Copyright (c) 2015-2016 The Khronos Group Inc.
* Copyright (c) 2015-2016 Valve Corporation
* Copyright (c) 2015-2016 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef VKICD_H
#define VKICD_H
#include "vulkan.h"
#include <stdbool.h>
// Loader-ICD version negotiation API. Versions add the following features:
// Version 0 - Initial. Doesn't support vk_icdGetInstanceProcAddr
// or vk_icdNegotiateLoaderICDInterfaceVersion.
// Version 1 - Add support for vk_icdGetInstanceProcAddr.
// Version 2 - Add Loader/ICD Interface version negotiation
// via vk_icdNegotiateLoaderICDInterfaceVersion.
// Version 3 - Add ICD creation/destruction of KHR_surface objects.
// Version 4 - Add unknown physical device extension qyering via
// vk_icdGetPhysicalDeviceProcAddr.
// Version 5 - Tells ICDs that the loader is now paying attention to the
// application version of Vulkan passed into the ApplicationInfo
// structure during vkCreateInstance. This will tell the ICD
// that if the loader is older, it should automatically fail a
// call for any API version > 1.0. Otherwise, the loader will
// manually determine if it can support the expected version.
#define CURRENT_LOADER_ICD_INTERFACE_VERSION 5
#define MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION 0
#define MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION 4
typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderICDInterfaceVersion)(uint32_t *pVersion);
// This is defined in vk_layer.h which will be found by the loader, but if an ICD is building against this
// flie directly, it won't be found.
#ifndef PFN_GetPhysicalDeviceProcAddr
typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);
#endif
/*
* The ICD must reserve space for a pointer for the loader's dispatch
* table, at the start of <each object>.
* The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro.
*/
#define ICD_LOADER_MAGIC 0x01CDC0DE
typedef union {
uintptr_t loaderMagic;
void *loaderData;
} VK_LOADER_DATA;
static inline void set_loader_magic_value(void *pNewObject) {
VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject;
loader_info->loaderMagic = ICD_LOADER_MAGIC;
}
static inline bool valid_loader_magic_value(void *pNewObject) {
const VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject;
return (loader_info->loaderMagic & 0xffffffff) == ICD_LOADER_MAGIC;
}
/*
* Windows and Linux ICDs will treat VkSurfaceKHR as a pointer to a struct that
* contains the platform-specific connection and surface information.
*/
typedef enum {
VK_ICD_WSI_PLATFORM_MIR,
VK_ICD_WSI_PLATFORM_WAYLAND,
VK_ICD_WSI_PLATFORM_WIN32,
VK_ICD_WSI_PLATFORM_XCB,
VK_ICD_WSI_PLATFORM_XLIB,
VK_ICD_WSI_PLATFORM_DISPLAY
} VkIcdWsiPlatform;
typedef struct {
VkIcdWsiPlatform platform;
} VkIcdSurfaceBase;
#ifdef VK_USE_PLATFORM_MIR_KHR
typedef struct {
VkIcdSurfaceBase base;
MirConnection *connection;
MirSurface *mirSurface;
} VkIcdSurfaceMir;
#endif // VK_USE_PLATFORM_MIR_KHR
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
typedef struct {
VkIcdSurfaceBase base;
struct wl_display *display;
struct wl_surface *surface;
} VkIcdSurfaceWayland;
#endif // VK_USE_PLATFORM_WAYLAND_KHR
#ifdef VK_USE_PLATFORM_WIN32_KHR
typedef struct {
VkIcdSurfaceBase base;
HINSTANCE hinstance;
HWND hwnd;
} VkIcdSurfaceWin32;
#endif // VK_USE_PLATFORM_WIN32_KHR
#ifdef VK_USE_PLATFORM_XCB_KHR
typedef struct {
VkIcdSurfaceBase base;
xcb_connection_t *connection;
xcb_window_t window;
} VkIcdSurfaceXcb;
#endif // VK_USE_PLATFORM_XCB_KHR
#ifdef VK_USE_PLATFORM_XLIB_KHR
typedef struct {
VkIcdSurfaceBase base;
Display *dpy;
Window window;
} VkIcdSurfaceXlib;
#endif // VK_USE_PLATFORM_XLIB_KHR
#ifdef VK_USE_PLATFORM_ANDROID_KHR
typedef struct {
ANativeWindow* window;
} VkIcdSurfaceAndroid;
#endif //VK_USE_PLATFORM_ANDROID_KHR
typedef struct {
VkIcdSurfaceBase base;
VkDisplayModeKHR displayMode;
uint32_t planeIndex;
uint32_t planeStackIndex;
VkSurfaceTransformFlagBitsKHR transform;
float globalAlpha;
VkDisplayPlaneAlphaFlagBitsKHR alphaMode;
VkExtent2D imageExtent;
} VkIcdSurfaceDisplay;
#endif // VKICD_H

143
include/vulkan/vk_layer.h Normal file
View File

@ -0,0 +1,143 @@
//
// File: vk_layer.h
//
/*
* Copyright (c) 2015-2017 The Khronos Group Inc.
* Copyright (c) 2015-2017 Valve Corporation
* Copyright (c) 2015-2017 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/* Need to define dispatch table
* Core struct can then have ptr to dispatch table at the top
* Along with object ptrs for current and next OBJ
*/
#pragma once
#include "vulkan.h"
#if defined(__GNUC__) && __GNUC__ >= 4
#define VK_LAYER_EXPORT __attribute__((visibility("default")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
#define VK_LAYER_EXPORT __attribute__((visibility("default")))
#else
#define VK_LAYER_EXPORT
#endif
// Definition for VkLayerDispatchTable and VkLayerInstanceDispatchTable now appear in externally generated header
#include "vk_layer_dispatch_table.h"
#define MAX_NUM_UNKNOWN_EXTS 250
// Loader-Layer version negotiation API. Versions add the following features:
// Versions 0/1 - Initial. Doesn't support vk_layerGetPhysicalDeviceProcAddr
// or vk_icdNegotiateLoaderLayerInterfaceVersion.
// Version 2 - Add support for vk_layerGetPhysicalDeviceProcAddr and
// vk_icdNegotiateLoaderLayerInterfaceVersion.
#define CURRENT_LOADER_LAYER_INTERFACE_VERSION 2
#define MIN_SUPPORTED_LOADER_LAYER_INTERFACE_VERSION 1
// Version negotiation values
typedef enum VkNegotiateLayerStructType {
LAYER_NEGOTIATE_UNINTIALIZED = 0,
LAYER_NEGOTIATE_INTERFACE_STRUCT = 1,
} VkNegotiateLayerStructType;
// Version negotiation structures
typedef struct VkNegotiateLayerInterface {
VkNegotiateLayerStructType sType;
void *pNext;
uint32_t loaderLayerInterfaceVersion;
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr;
PFN_GetPhysicalDeviceProcAddr pfnGetPhysicalDeviceProcAddr;
} VkNegotiateLayerInterface;
// Version negotiation functions
typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderLayerInterfaceVersion)(VkNegotiateLayerInterface *pVersionStruct);
// Function prototype for unknown physical device extension command
typedef VkResult(VKAPI_PTR *PFN_PhysDevExt)(VkPhysicalDevice phys_device);
// ------------------------------------------------------------------------------------------------
// CreateInstance and CreateDevice support structures
/* Sub type of structure for instance and device loader ext of CreateInfo.
* When sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
* or sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
* then VkLayerFunction indicates struct type pointed to by pNext
*/
typedef enum VkLayerFunction_ {
VK_LAYER_LINK_INFO = 0,
VK_LOADER_DATA_CALLBACK = 1
} VkLayerFunction;
typedef struct VkLayerInstanceLink_ {
struct VkLayerInstanceLink_ *pNext;
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
PFN_GetPhysicalDeviceProcAddr pfnNextGetPhysicalDeviceProcAddr;
} VkLayerInstanceLink;
/*
* When creating the device chain the loader needs to pass
* down information about it's device structure needed at
* the end of the chain. Passing the data via the
* VkLayerDeviceInfo avoids issues with finding the
* exact instance being used.
*/
typedef struct VkLayerDeviceInfo_ {
void *device_info;
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
} VkLayerDeviceInfo;
typedef VkResult (VKAPI_PTR *PFN_vkSetInstanceLoaderData)(VkInstance instance,
void *object);
typedef VkResult (VKAPI_PTR *PFN_vkSetDeviceLoaderData)(VkDevice device,
void *object);
typedef struct {
VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
const void *pNext;
VkLayerFunction function;
union {
VkLayerInstanceLink *pLayerInfo;
PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
} u;
} VkLayerInstanceCreateInfo;
typedef struct VkLayerDeviceLink_ {
struct VkLayerDeviceLink_ *pNext;
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr;
} VkLayerDeviceLink;
typedef struct {
VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
const void *pNext;
VkLayerFunction function;
union {
VkLayerDeviceLink *pLayerInfo;
PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
} u;
} VkLayerDeviceCreateInfo;
#ifdef __cplusplus
extern "C" {
#endif
VKAPI_ATTR VkResult VKAPI_CALL vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,120 @@
//
// File: vk_platform.h
//
/*
** Copyright (c) 2014-2017 The Khronos Group Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef VK_PLATFORM_H_
#define VK_PLATFORM_H_
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
/*
***************************************************************************************************
* Platform-specific directives and type declarations
***************************************************************************************************
*/
/* Platform-specific calling convention macros.
*
* Platforms should define these so that Vulkan clients call Vulkan commands
* with the same calling conventions that the Vulkan implementation expects.
*
* VKAPI_ATTR - Placed before the return type in function declarations.
* Useful for C++11 and GCC/Clang-style function attribute syntax.
* VKAPI_CALL - Placed after the return type in function declarations.
* Useful for MSVC-style calling convention syntax.
* VKAPI_PTR - Placed between the '(' and '*' in function pointer types.
*
* Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void);
* Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
*/
#if defined(_WIN32)
// On Windows, Vulkan commands use the stdcall convention
#define VKAPI_ATTR
#define VKAPI_CALL __stdcall
#define VKAPI_PTR VKAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
#error "Vulkan isn't supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
// On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
// calling convention, i.e. float parameters are passed in registers. This
// is true even if the rest of the application passes floats on the stack,
// as it does by default when compiling for the armeabi-v7a NDK ABI.
#define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define VKAPI_CALL
#define VKAPI_PTR VKAPI_ATTR
#else
// On other platforms, use the default calling convention
#define VKAPI_ATTR
#define VKAPI_CALL
#define VKAPI_PTR
#endif
#include <stddef.h>
#if !defined(VK_NO_STDINT_H)
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#endif // !defined(VK_NO_STDINT_H)
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
// Platform-specific headers required by platform window system extensions.
// These are enabled prior to #including "vulkan.h". The same enable then
// controls inclusion of the extension interfaces in vulkan.h.
#ifdef VK_USE_PLATFORM_ANDROID_KHR
#include <android/native_window.h>
#endif
#ifdef VK_USE_PLATFORM_MIR_KHR
#include <mir_toolkit/client_types.h>
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include <wayland-client.h>
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
#include <windows.h>
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
#include <X11/Xlib.h>
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
#include <xcb/xcb.h>
#endif
#endif

View File

@ -0,0 +1,46 @@
//
// File: vk_sdk_platform.h
//
/*
* Copyright (c) 2015-2016 The Khronos Group Inc.
* Copyright (c) 2015-2016 Valve Corporation
* Copyright (c) 2015-2016 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VK_SDK_PLATFORM_H
#define VK_SDK_PLATFORM_H
#if defined(_WIN32)
#define NOMINMAX
#ifndef __cplusplus
#undef inline
#define inline __inline
#endif // __cplusplus
#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/)
// C99:
// Microsoft didn't implement C99 in Visual Studio; but started adding it with
// VS2013. However, VS2013 still didn't have snprintf(). The following is a
// work-around (Note: The _CRT_SECURE_NO_WARNINGS macro must be set in the
// "CMakeLists.txt" file).
// NOTE: This is fixed in Visual Studio 2015.
#define snprintf _snprintf
#endif
#define strdup _strdup
#endif // _WIN32
#endif // VK_SDK_PLATFORM_H

6429
include/vulkan/vulkan.h Normal file

File diff suppressed because it is too large Load Diff

32807
include/vulkan/vulkan.hpp Normal file

File diff suppressed because it is too large Load Diff

BIN
lib/SDL2.lib Executable file

Binary file not shown.

BIN
lib/vulkan-1.lib Executable file

Binary file not shown.

11
meson.build Normal file
View File

@ -0,0 +1,11 @@
project('dxvk', ['cpp'])
dxvk_compiler = meson.get_compiler('cpp')
dxvk_library_path = meson.source_root() + '/lib'
dxvk_include_path = include_directories('./include')
lib_vulkan = dxvk_compiler.find_library('vulkan-1', dirs : dxvk_library_path)
lib_sdl2 = dxvk_compiler.find_library('SDL2', dirs : dxvk_library_path)
subdir('src')
subdir('tests')

177
src/dxvk/dxvk_adapter.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <cstring>
#include "dxvk_adapter.h"
#include "dxvk_device.h"
#include "dxvk_instance.h"
#include "dxvk_surface.h"
namespace dxvk {
DxvkAdapter::DxvkAdapter(
const Rc<DxvkInstance>& instance,
VkPhysicalDevice handle)
: m_instance (instance),
m_vki (instance->vki()),
m_handle (handle) {
uint32_t numQueueFamilies = 0;
m_vki->vkGetPhysicalDeviceQueueFamilyProperties(
m_handle, &numQueueFamilies, nullptr);
m_queueFamilies.resize(numQueueFamilies);
m_vki->vkGetPhysicalDeviceQueueFamilyProperties(
m_handle, &numQueueFamilies, m_queueFamilies.data());
}
DxvkAdapter::~DxvkAdapter() {
}
VkPhysicalDeviceProperties DxvkAdapter::deviceProperties() const {
VkPhysicalDeviceProperties properties;
m_vki->vkGetPhysicalDeviceProperties(m_handle, &properties);
return properties;
}
VkPhysicalDeviceMemoryProperties DxvkAdapter::memoryProperties() const {
VkPhysicalDeviceMemoryProperties memoryProperties;
m_vki->vkGetPhysicalDeviceMemoryProperties(m_handle, &memoryProperties);
return memoryProperties;
}
VkPhysicalDeviceFeatures DxvkAdapter::features() const {
VkPhysicalDeviceFeatures features;
m_vki->vkGetPhysicalDeviceFeatures(m_handle, &features);
return features;
}
VkFormatProperties DxvkAdapter::formatProperties(VkFormat format) const {
VkFormatProperties formatProperties;
m_vki->vkGetPhysicalDeviceFormatProperties(m_handle, format, &formatProperties);
return formatProperties;
}
std::optional<VkImageFormatProperties> DxvkAdapter::imageFormatProperties(
VkFormat format,
VkImageType type,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageCreateFlags flags) const {
VkImageFormatProperties formatProperties;
VkResult status = m_vki->vkGetPhysicalDeviceImageFormatProperties(
m_handle, format, type, tiling, usage, flags, &formatProperties);
switch (status) {
case VK_SUCCESS: return formatProperties;
case VK_ERROR_FORMAT_NOT_SUPPORTED: return { };
default:
throw DxvkError("DxvkAdapter::imageFormatProperties: Failed to query format properties");
}
}
uint32_t DxvkAdapter::graphicsQueueFamily() const {
for (uint32_t i = 0; i < m_queueFamilies.size(); i++) {
if (m_queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
return i;
}
throw DxvkError("DxvkAdapter::graphicsQueueFamily: No graphics queue found");
}
uint32_t DxvkAdapter::presentQueueFamily() const {
// TODO Implement properly
return this->graphicsQueueFamily();
}
Rc<DxvkDevice> DxvkAdapter::createDevice() {
auto enabledExtensions = this->enableExtensions();
auto enabledFeatures = this->enableFeatures();
float queuePriority = 1.0f;
std::vector<VkDeviceQueueCreateInfo> queueInfos;
const uint32_t gIndex = this->graphicsQueueFamily();
const uint32_t pIndex = this->presentQueueFamily();
VkDeviceQueueCreateInfo graphicsQueue;
graphicsQueue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
graphicsQueue.pNext = nullptr;
graphicsQueue.flags = 0;
graphicsQueue.queueFamilyIndex = gIndex;
graphicsQueue.queueCount = 1;
graphicsQueue.pQueuePriorities = &queuePriority;
queueInfos.push_back(graphicsQueue);
if (pIndex != gIndex) {
VkDeviceQueueCreateInfo presentQueue = graphicsQueue;
presentQueue.queueFamilyIndex = pIndex;
queueInfos.push_back(presentQueue);
}
VkDeviceCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.queueCreateInfoCount = queueInfos.size();
info.pQueueCreateInfos = queueInfos.data();
info.enabledLayerCount = 0;
info.ppEnabledLayerNames = nullptr;
info.enabledExtensionCount = enabledExtensions.count();
info.ppEnabledExtensionNames = enabledExtensions.names();
info.pEnabledFeatures = &enabledFeatures;
VkDevice device = VK_NULL_HANDLE;
if (m_vki->vkCreateDevice(m_handle, &info, nullptr, &device) != VK_SUCCESS)
throw DxvkError("DxvkDevice::createDevice: Failed to create device");
return new DxvkDevice(this, new vk::DeviceFn(m_vki->instance(), device));
}
Rc<DxvkSurface> DxvkAdapter::createSurface(HINSTANCE instance, HWND window) {
return new DxvkSurface(this, instance, window);
}
vk::NameList DxvkAdapter::enableExtensions() {
std::vector<const char*> extOptional = { };
std::vector<const char*> extRequired = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
const vk::NameSet extensionsAvailable
= vk::NameSet::enumerateDeviceExtensions(*m_vki, m_handle);
vk::NameList extensionsEnabled;
for (auto e : extOptional) {
if (extensionsAvailable.supports(e))
extensionsEnabled.add(e);
}
for (auto e : extRequired) {
if (!extensionsAvailable.supports(e))
throw DxvkError(str::format("DxvkDevice::getExtensions: Extension ", e, " not supported"));
extensionsEnabled.add(e);
}
return extensionsEnabled;
}
VkPhysicalDeviceFeatures DxvkAdapter::enableFeatures() {
VkPhysicalDeviceFeatures features;
std::memset(&features, 0, sizeof(features));
return features;
}
}

143
src/dxvk/dxvk_adapter.h Normal file
View File

@ -0,0 +1,143 @@
#pragma once
#include <optional>
#include "./vulkan/dxvk_vulkan_extensions.h"
#include "dxvk_include.h"
namespace dxvk {
class DxvkDevice;
class DxvkInstance;
class DxvkSurface;
/**
* \brief DXVK adapter
*
* Corresponds to a physical device in Vulkan. Provides
* all kinds of information about the device itself and
* the supported feature set.
*/
class DxvkAdapter : public RcObject {
public:
DxvkAdapter(
const Rc<DxvkInstance>& instance,
VkPhysicalDevice handle);
~DxvkAdapter();
/**
* \brief Vulkan instance functions
* \returns Vulkan instance functions
*/
Rc<vk::InstanceFn> vki() const {
return m_vki;
}
/**
* \brief Physical device handle
* \returns The adapter handle
*/
VkPhysicalDevice handle() const {
return m_handle;
}
/**
* \brief Physical device properties
*
* Retrieves information about the device itself.
* \returns Physical device properties
*/
VkPhysicalDeviceProperties deviceProperties() const;
/**
* \brief Memory properties
*
* Queries the memory types and memory heaps of
* the device. This is useful for memory allocators.
* \returns Device memory properties
*/
VkPhysicalDeviceMemoryProperties memoryProperties() const;
/**
* \brief Supportred device features
*
* Queries the supported device features.
* \returns Device features
*/
VkPhysicalDeviceFeatures features() const;
/**
* \brief Queries format support
*
* \param [in] format The format to query
* \returns Format support info
*/
VkFormatProperties formatProperties(
VkFormat format) const;
/**
* \brief Queries image format support
*
* \param [in] format Format to query
* \param [in] type Image type
* \param [in] tiling Image tiling
* \param [in] usage Image usage flags
* \param [in] flags Image create flags
* \returns Image format support info
*/
std::optional<VkImageFormatProperties> imageFormatProperties(
VkFormat format,
VkImageType type,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageCreateFlags flags) const;
/**
* \brief Graphics queue family index
* \returns Graphics queue family index
*/
uint32_t graphicsQueueFamily() const;
/**
* \brief Presentation queue family index
* \returns Presentation queue family index
*/
uint32_t presentQueueFamily() const;
/**
* \brief Creates a DXVK device
*
* Creates a logical device for this adapter.
* \returns Device handle
*/
Rc<DxvkDevice> createDevice();
/**
* \brief Creates a surface
*
* \param [in] instance Module instance
* \param [in] window Application window
* \returns Surface handle
*/
Rc<DxvkSurface> createSurface(
HINSTANCE instance,
HWND window);
private:
Rc<DxvkInstance> m_instance;
Rc<vk::InstanceFn> m_vki;
VkPhysicalDevice m_handle;
std::vector<VkQueueFamilyProperties> m_queueFamilies;
vk::NameList enableExtensions();
VkPhysicalDeviceFeatures enableFeatures();
};
}

0
src/dxvk/dxvk_buffer.cpp Normal file
View File

38
src/dxvk/dxvk_buffer.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "dxvk_resource.h"
namespace dxvk {
/**
* \brief Buffer create info
*
* The properties of a buffer that are
* passed to \ref DxvkDevice::createBuffer
*/
struct DxvkBufferCreateInfo {
/// Size of the buffer, in bytes
VkDeviceSize bufferSize;
};
/**
* \brief DXVK buffer
*
* A simple buffer resource that stores linear data.
*/
class DxvkBuffer : public DxvkResource {
public:
private:
};
}

68
src/dxvk/dxvk_cmdlist.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "dxvk_cmdlist.h"
namespace dxvk {
DxvkCommandList::DxvkCommandList(
const Rc<vk::DeviceFn>& vkd,
uint32_t queueFamily)
: m_vkd(vkd) {
VkCommandPoolCreateInfo poolInfo;
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.pNext = nullptr;
poolInfo.flags = 0;
poolInfo.queueFamilyIndex = queueFamily;
if (m_vkd->vkCreateCommandPool(m_vkd->device(), &poolInfo, nullptr, &m_pool) != VK_SUCCESS)
throw DxvkError("DxvkCommandList::DxvkCommandList: Failed to create command pool");
VkCommandBufferAllocateInfo cmdInfo;
cmdInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdInfo.pNext = nullptr;
cmdInfo.commandPool = m_pool;
cmdInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdInfo.commandBufferCount = 1;
if (m_vkd->vkAllocateCommandBuffers(m_vkd->device(), &cmdInfo, &m_buffer) != VK_SUCCESS)
throw DxvkError("DxvkCommandList::DxvkCommandList: Failed to allocate command buffer");
}
DxvkCommandList::~DxvkCommandList() {
m_resources.reset();
m_vkd->vkDestroyCommandPool(
m_vkd->device(), m_pool, nullptr);
}
void DxvkCommandList::beginRecording() {
VkCommandBufferBeginInfo info;
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
info.pNext = nullptr;
info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
info.pInheritanceInfo = nullptr;
if (m_vkd->vkResetCommandPool(m_vkd->device(), m_pool, 0) != VK_SUCCESS)
throw DxvkError("DxvkCommandList::beginRecording: Failed to reset command pool");
if (m_vkd->vkBeginCommandBuffer(m_buffer, &info) != VK_SUCCESS)
throw DxvkError("DxvkCommandList::beginRecording: Failed to begin command buffer recording");
}
void DxvkCommandList::endRecording() {
if (m_vkd->vkEndCommandBuffer(m_buffer) != VK_SUCCESS)
throw DxvkError("DxvkCommandList::endRecording: Failed to record command buffer");
}
void DxvkCommandList::trackResource(const Rc<DxvkResource>& rc) {
m_resources.trackResource(rc);
}
void DxvkCommandList::reset() {
m_resources.reset();
}
}

70
src/dxvk/dxvk_cmdlist.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include <unordered_set>
#include "dxvk_lifetime.h"
namespace dxvk {
/**
* \brief DXVK command list
*
* Stores a command buffer that a context can use to record Vulkan
* commands. The command list shall also reference the resources
* used by the recorded commands for automatic lifetime tracking.
* When the command list has completed execution, resources that
* are no longer used may get destroyed.
*/
class DxvkCommandList : public RcObject {
public:
DxvkCommandList(
const Rc<vk::DeviceFn>& vkd,
uint32_t queueFamily);
~DxvkCommandList();
/**
* \brief Command buffer handle
* \returns Command buffer handle
*/
VkCommandBuffer handle() const {
return m_buffer;
}
void beginRecording();
void endRecording();
/**
* \brief Adds a resource to track
*
* Adds a resource to the internal resource tracker.
* Resources will be kept alive and "in use" until
* the device can guarantee that the submission has
* completed.
*/
void trackResource(
const Rc<DxvkResource>& rc);
/**
* \brief Resets the command list
*
* Resets the internal command buffer of the command list and
* marks all tracked resources as unused. When submitting the
* command list to the device, this method will be called once
* the command list completes execution.
*/
void reset();
private:
Rc<vk::DeviceFn> m_vkd;
VkCommandPool m_pool;
VkCommandBuffer m_buffer;
DxvkLifetimeTracker m_resources;
};
}

73
src/dxvk/dxvk_context.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "dxvk_context.h"
#include "dxvk_main.h"
namespace dxvk {
DxvkContext::DxvkContext(const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) { }
DxvkContext::~DxvkContext() {
}
void DxvkContext::beginRecording(
const Rc<DxvkCommandList>& cmdList) {
m_commandList = cmdList;
m_commandList->beginRecording();
}
bool DxvkContext::endRecording() {
m_commandList->endRecording();
m_commandList = nullptr;
return true;
}
void DxvkContext::setFramebuffer(
const Rc<DxvkFramebuffer>& fb) {
const DxvkFramebufferSize fbSize = fb->size();
// TODO implement properly
VkRect2D renderArea;
renderArea.offset.x = 0;
renderArea.offset.y = 0;
renderArea.extent.width = fbSize.width;
renderArea.extent.height = fbSize.height;
VkRenderPassBeginInfo info;
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.pNext = nullptr;
info.renderPass = fb->renderPass();
info.framebuffer = fb->handle();
info.renderArea = renderArea;
info.clearValueCount = 0;
info.pClearValues = nullptr;
// This is for testing purposes only.
VkClearAttachment attachment;
attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
attachment.colorAttachment = 0;
attachment.clearValue.color.float32[0] = 1.0f;
attachment.clearValue.color.float32[1] = 1.0f;
attachment.clearValue.color.float32[2] = 1.0f;
attachment.clearValue.color.float32[3] = 1.0f;
VkClearRect clearRect;
clearRect.rect = renderArea;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = fbSize.layers;
m_vkd->vkCmdBeginRenderPass(
m_commandList->handle(), &info,
VK_SUBPASS_CONTENTS_INLINE);
m_vkd->vkCmdClearAttachments(
m_commandList->handle(),
1, &attachment,
1, &clearRect);
m_vkd->vkCmdEndRenderPass(
m_commandList->handle());
}
}

66
src/dxvk/dxvk_context.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "dxvk_cmdlist.h"
#include "dxvk_framebuffer.h"
namespace dxvk {
/**
* \brief DXVk context
*
* Tracks pipeline state and records command lists.
* This is where the actual rendering commands are
* recorded.
*/
class DxvkContext : public RcObject {
public:
DxvkContext(const Rc<vk::DeviceFn>& vkd);
~DxvkContext();
/**
* \brief Begins command buffer recording
*
* Begins recording a command list. This does
* not alter any context state other than the
* active command list.
* \param [in] cmdList Target command list
*/
void beginRecording(
const Rc<DxvkCommandList>& cmdList);
/**
* \brief Ends command buffer recording
*
* Finishes recording the active command list.
* The command list can then be submitted to
* the device.
*
* The return value of this method can be used to
* determine whether the command list needs to be
* submitted. In case the command list is empty,
* \c false will be returned and it shall not be
* submitted to the device.
*
* This will not change any context state
* other than the active command list.
* \returns \c true if any commands were recorded
*/
bool endRecording();
/**
* \brief Sets framebuffer
* \param [in] fb Framebuffer
*/
void setFramebuffer(
const Rc<DxvkFramebuffer>& fb);
private:
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkCommandList> m_commandList;
};
}

View File

View File

@ -0,0 +1,9 @@
#pragma once
#include "dxvk_cmdlist.h"
namespace dxvk {
}

119
src/dxvk/dxvk_device.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "dxvk_device.h"
#include "dxvk_instance.h"
namespace dxvk {
DxvkDevice::DxvkDevice(
const Rc<DxvkAdapter>& adapter,
const Rc<vk::DeviceFn>& vkd)
: m_adapter (adapter),
m_vkd (vkd),
m_memory (adapter, vkd),
m_renderPassPool(vkd) {
m_vkd->vkGetDeviceQueue(m_vkd->device(),
m_adapter->graphicsQueueFamily(), 0,
&m_graphicsQueue);
m_vkd->vkGetDeviceQueue(m_vkd->device(),
m_adapter->presentQueueFamily(), 0,
&m_presentQueue);
}
DxvkDevice::~DxvkDevice() {
m_vkd->vkDeviceWaitIdle(m_vkd->device());
m_vkd->vkDestroyDevice(m_vkd->device(), nullptr);
}
Rc<DxvkCommandList> DxvkDevice::createCommandList() {
return new DxvkCommandList(m_vkd,
m_adapter->graphicsQueueFamily());
}
Rc<DxvkContext> DxvkDevice::createContext() {
return new DxvkContext(m_vkd);
}
Rc<DxvkFramebuffer> DxvkDevice::createFramebuffer(
const DxvkRenderTargets& renderTargets) {
auto format = renderTargets.renderPassFormat();
auto renderPass = m_renderPassPool.getRenderPass(format);
return new DxvkFramebuffer(m_vkd, renderPass, renderTargets);
}
Rc<DxvkImage> DxvkDevice::createImage(
const DxvkImageCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType) {
}
Rc<DxvkImageView> DxvkDevice::createImageView(
const Rc<DxvkImage>& image,
const DxvkImageViewCreateInfo& createInfo) {
return new DxvkImageView(m_vkd, image, createInfo);
}
Rc<DxvkSemaphore> DxvkDevice::createSemaphore() {
return new DxvkSemaphore(m_vkd);
}
Rc<DxvkSwapchain> DxvkDevice::createSwapchain(
const Rc<DxvkSurface>& surface,
const DxvkSwapchainProperties& properties) {
return new DxvkSwapchain(this, surface, properties, m_presentQueue);
}
Rc<DxvkFence> DxvkDevice::submitCommandList(
const Rc<DxvkCommandList>& commandList,
const Rc<DxvkSemaphore>& waitSync,
const Rc<DxvkSemaphore>& wakeSync) {
Rc<DxvkFence> fence = new DxvkFence(m_vkd);
VkCommandBuffer commandBuffer = commandList->handle();
VkSemaphore waitSemaphore = VK_NULL_HANDLE;
VkSemaphore wakeSemaphore = VK_NULL_HANDLE;
if (waitSync != nullptr) {
waitSemaphore = waitSync->handle();
commandList->trackResource(waitSync);
}
if (wakeSync != nullptr) {
wakeSemaphore = wakeSync->handle();
commandList->trackResource(wakeSync);
}
const VkPipelineStageFlags waitStageMask
= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkSubmitInfo info;
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
info.pNext = nullptr;
info.waitSemaphoreCount = waitSemaphore == VK_NULL_HANDLE ? 0 : 1;
info.pWaitSemaphores = &waitSemaphore;
info.pWaitDstStageMask = &waitStageMask;
info.commandBufferCount = commandBuffer == VK_NULL_HANDLE ? 0 : 1;
info.pCommandBuffers = &commandBuffer;
info.signalSemaphoreCount = wakeSemaphore == VK_NULL_HANDLE ? 0 : 1;
info.pSignalSemaphores = &wakeSemaphore;
if (m_vkd->vkQueueSubmit(m_graphicsQueue, 1, &info, fence->handle()) != VK_SUCCESS)
throw DxvkError("DxvkDevice::submitCommandList: Command submission failed");
return fence;
}
void DxvkDevice::waitForIdle() const {
if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
throw DxvkError("DxvkDevice::waitForIdle: Operation failed");
}
}

152
src/dxvk/dxvk_device.h Normal file
View File

@ -0,0 +1,152 @@
#pragma once
#include "dxvk_adapter.h"
#include "dxvk_buffer.h"
#include "dxvk_context.h"
#include "dxvk_framebuffer.h"
#include "dxvk_memory.h"
#include "dxvk_renderpass.h"
#include "dxvk_swapchain.h"
#include "dxvk_sync.h"
namespace dxvk {
class DxvkInstance;
/**
* \brief DXVK device
*
* Device object. This is responsible for resource creation,
* memory allocation, command submission and state tracking.
* Rendering commands are recorded into command lists using
* contexts. Multiple contexts can be created for a device.
*/
class DxvkDevice : public RcObject {
public:
DxvkDevice(
const Rc<DxvkAdapter>& adapter,
const Rc<vk::DeviceFn>& vkd);
~DxvkDevice();
/**
* \brief Vulkan device functions
* \returns Vulkan device functions
*/
Rc<vk::DeviceFn> vkd() const {
return m_vkd;
}
/**
* \brief Logical device handle
* \returns The device handle
*/
VkDevice handle() const {
return m_vkd->device();
}
/**
* \brief Creates a command list
* \returns The command list
*/
Rc<DxvkCommandList> createCommandList();
/**
* \brief Creates a context
*
* Creates a context object that can
* be used to record command buffers.
* \returns The context object
*/
Rc<DxvkContext> createContext();
/**
* \brief Creates framebuffer for a set of render targets
*
* Automatically deduces framebuffer dimensions
* from the supplied render target views.
* \param [in] renderTargets Render targets
* \returns The framebuffer object
*/
Rc<DxvkFramebuffer> createFramebuffer(
const DxvkRenderTargets& renderTargets);
/**
* \brief Creates an image object
*
* \param [in] createInfo Image create info
* \param [in] memoryType Memory type flags
* \returns The image object
*/
Rc<DxvkImage> createImage(
const DxvkImageCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType);
/**
* \brief Creates an image view
*
* \param [in] image The image to create a view for
* \param [in] createInfo Image view create info
* \returns The image view
*/
Rc<DxvkImageView> createImageView(
const Rc<DxvkImage>& image,
const DxvkImageViewCreateInfo& createInfo);
/**
* \brief Creates a semaphore object
* \returns Newly created semaphore
*/
Rc<DxvkSemaphore> createSemaphore();
/**
* \brief Creates a swap chain
*
* \param [in] surface The target surface
* \param [in] properties Swapchain properties
* \returns The swapchain object
*/
Rc<DxvkSwapchain> createSwapchain(
const Rc<DxvkSurface>& surface,
const DxvkSwapchainProperties& properties);
/**
* \brief Submits a command list
*
* Synchronization arguments are optional.
* \param [in] commandList The command list to submit
* \param [in] waitSync (Optional) Semaphore to wait on
* \param [in] wakeSync (Optional) Semaphore to notify
* \returns Synchronization fence
*/
Rc<DxvkFence> submitCommandList(
const Rc<DxvkCommandList>& commandList,
const Rc<DxvkSemaphore>& waitSync,
const Rc<DxvkSemaphore>& wakeSync);
/**
* \brief Waits until the device becomes idle
*
* Waits for the GPU to complete the execution of all
* previously submitted command buffers. This may be
* used to ensure that resources that were previously
* used by the GPU can be safely destroyed.
*/
void waitForIdle() const;
private:
Rc<DxvkAdapter> m_adapter;
Rc<vk::DeviceFn> m_vkd;
DxvkMemoryAllocator m_memory;
DxvkRenderPassPool m_renderPassPool;
VkQueue m_graphicsQueue;
VkQueue m_presentQueue;
};
}

View File

@ -0,0 +1,95 @@
#include "dxvk_framebuffer.h"
namespace dxvk {
DxvkRenderTargets:: DxvkRenderTargets() { }
DxvkRenderTargets::~DxvkRenderTargets() { }
DxvkRenderPassFormat DxvkRenderTargets::renderPassFormat() const {
DxvkRenderPassFormat result;
for (uint32_t i = 0; i < MaxNumColorTargets; i++) {
if (m_colorTargets.at(i) != nullptr) {
result.setColorFormat(i, m_colorTargets.at(i)->info().format);
result.setSampleCount(m_colorTargets.at(i)->imageInfo().sampleCount);
}
}
if (m_depthTarget != nullptr) {
result.setDepthFormat(m_depthTarget->info().format);
result.setSampleCount(m_depthTarget->imageInfo().sampleCount);
}
return result;
}
std::vector<VkImageView> DxvkRenderTargets::getAttachments() const {
std::vector<VkImageView> result;
if (m_depthTarget != nullptr)
result.push_back(m_depthTarget->handle());
for (uint32_t i = 0; i < MaxNumColorTargets; i++) {
if (m_colorTargets.at(i) != nullptr)
result.push_back(m_colorTargets.at(i)->handle());
}
return result;
}
DxvkFramebufferSize DxvkRenderTargets::getImageSize() const {
if (m_depthTarget != nullptr)
return this->renderTargetSize(m_depthTarget);
for (uint32_t i = 0; i < MaxNumColorTargets; i++) {
if (m_colorTargets.at(i) != nullptr)
return this->renderTargetSize(m_colorTargets.at(i));
}
return DxvkFramebufferSize { 0, 0, 0 };
}
DxvkFramebufferSize DxvkRenderTargets::renderTargetSize(
const Rc<DxvkImageView>& renderTarget) const {
auto extent = renderTarget->imageInfo().extent;
auto layers = renderTarget->info().numLayers;
return DxvkFramebufferSize { extent.width, extent.height, layers };
}
DxvkFramebuffer::DxvkFramebuffer(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkRenderPass>& renderPass,
const DxvkRenderTargets& renderTargets)
: m_vkd (vkd),
m_renderPass (renderPass),
m_renderTargets (renderTargets),
m_framebufferSize (renderTargets.getImageSize()) {
auto views = renderTargets.getAttachments();
VkFramebufferCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.renderPass = renderPass->handle();
info.attachmentCount = views.size();
info.pAttachments = views.data();
info.width = m_framebufferSize.width;
info.height = m_framebufferSize.height;
info.layers = m_framebufferSize.layers;
if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &info, nullptr, &m_framebuffer) != VK_SUCCESS)
throw DxvkError("DxvkFramebuffer::DxvkFramebuffer: Failed to create framebuffer object");
}
DxvkFramebuffer::~DxvkFramebuffer() {
m_vkd->vkDestroyFramebuffer(
m_vkd->device(), m_framebuffer, nullptr);
}
}

160
src/dxvk/dxvk_framebuffer.h Normal file
View File

@ -0,0 +1,160 @@
#pragma once
#include "dxvk_image.h"
#include "dxvk_renderpass.h"
namespace dxvk {
/**
* \brief Framebuffer size
*
* Stores the width, height and number of layers
* of a framebuffer. This can be used in case a
* framebuffer does not have any attachments.
*/
struct DxvkFramebufferSize {
uint32_t width;
uint32_t height;
uint32_t layers;
};
/**
* \brief Render target description
*
* Stores render targets for a framebuffer object
* and provides methods to query the render pass
* format. Note that all render target views must
* have the same size and number of array layers.
*/
class DxvkRenderTargets {
public:
DxvkRenderTargets();
~DxvkRenderTargets();
/**
* \brief Retrieves color target
*
* \param [in] id Color attachment ID
* \returns Render target view
*/
Rc<DxvkImageView> getColorTarget(uint32_t id) const {
return m_colorTargets.at(id);
}
/**
* \brief Retrieves depth-stencil target
* \returns Depth-stencil target view
*/
Rc<DxvkImageView> getDepthTarget() const {
return m_depthTarget;
}
/**
* \brief Sets color target
*
* \param [in] id Color attachment ID
* \param [in] view Render target view
*/
void setColorTarget(uint32_t id, const Rc<DxvkImageView>& view) {
m_colorTargets.at(id) = view;
}
/**
* \brief Sets depth-stencil target
* \param [in] view Depth-stencil target view
*/
void setDepthTarget(const Rc<DxvkImageView>& view) {
m_depthTarget = view;
}
/**
* \brief Render pass format
*
* Computes the render pass format based on
* the color and depth-stencil attachments.
* \returns Render pass format
*/
DxvkRenderPassFormat renderPassFormat() const;
/**
* \brief Creates attachment list
* \returns Framebuffer attachment list
*/
std::vector<VkImageView> getAttachments() const;
/**
* \brief Framebuffer size
*
* The width, height and layers
* of the attached render targets.
* \returns Framebuffer size
*/
DxvkFramebufferSize getImageSize() const;
private:
std::array<Rc<DxvkImageView>, MaxNumColorTargets> m_colorTargets;
Rc<DxvkImageView> m_depthTarget;
DxvkFramebufferSize renderTargetSize(
const Rc<DxvkImageView>& renderTarget) const;
};
/**
* \brief DXVK framebuffer
*
* A framebuffer either stores a set of image views
* that will be used as render targets, or in case
* no render targets are being used, fixed viewport
* dimensions.
*/
class DxvkFramebuffer : public DxvkResource {
public:
DxvkFramebuffer(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkRenderPass>& renderPass,
const DxvkRenderTargets& renderTargets);
~DxvkFramebuffer();
/**
* \brief Framebuffer handle
* \returns Framebuffer handle
*/
VkFramebuffer handle() const {
return m_framebuffer;
}
/**
* \brief Render pass handle
* \returns Render pass handle
*/
VkRenderPass renderPass() const {
return m_renderPass->handle();
}
/**
* \brief Framebuffer size
* \returns Framebuffer size
*/
DxvkFramebufferSize size() const {
return m_framebufferSize;
}
private:
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkRenderPass> m_renderPass;
DxvkRenderTargets m_renderTargets;
DxvkFramebufferSize m_framebufferSize;
VkFramebuffer m_framebuffer;
};
}

34
src/dxvk/dxvk_hash.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <cstddef>
namespace dxvk {
struct DxvkHash {
template<typename T>
size_t operator () (const T& object) const {
return object.hash();
}
};
class DxvkHashState {
public:
void add(size_t hash) {
m_value ^= hash + 0x9e3779b9
+ (m_value << 6)
+ (m_value >> 2);
}
operator size_t () const {
return m_value;
}
private:
size_t m_value = 0;
};
}

66
src/dxvk/dxvk_image.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "dxvk_image.h"
namespace dxvk {
// DxvkImage::DxvkImage(
// const Rc<vk::DeviceFn>& vkd,
// const DxvkImageCreateInfo& info,
// DxvkMemory&& memory)
// : m_vkd(vkd), m_info(info), m_memory(std::move(memory)) {
//
// }
DxvkImage::DxvkImage(
const Rc<vk::DeviceFn>& vkd,
const DxvkImageCreateInfo& info,
VkImage image)
: m_vkd(vkd), m_info(info), m_image(image) { }
DxvkImage::~DxvkImage() {
// This is a bit of a hack to determine whether
// the image is implementation-handled or not
if (m_memory.memory() != VK_NULL_HANDLE)
m_vkd->vkDestroyImage(m_vkd->device(), m_image, nullptr);
}
DxvkImageView::DxvkImageView(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkImage>& image,
const DxvkImageViewCreateInfo& info)
: m_vkd(vkd), m_image(image), m_info(info) {
VkComponentMapping componentMapping;
componentMapping.r = VK_COMPONENT_SWIZZLE_IDENTITY;
componentMapping.g = VK_COMPONENT_SWIZZLE_IDENTITY;
componentMapping.b = VK_COMPONENT_SWIZZLE_IDENTITY;
componentMapping.a = VK_COMPONENT_SWIZZLE_IDENTITY;
VkImageSubresourceRange subresourceRange;
subresourceRange.aspectMask = info.aspect;
subresourceRange.baseMipLevel = info.minLevel;
subresourceRange.levelCount = info.numLevels;
subresourceRange.baseArrayLayer = info.minLayer;
subresourceRange.layerCount = info.numLayers;
VkImageViewCreateInfo viewInfo;
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = image->handle();
viewInfo.viewType = info.type;
viewInfo.format = info.format;
viewInfo.components = componentMapping;
viewInfo.subresourceRange = subresourceRange;
if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &m_view) != VK_SUCCESS)
throw DxvkError("DxvkImageView::DxvkImageView: Failed to create image view");
}
DxvkImageView::~DxvkImageView() {
m_vkd->vkDestroyImageView(m_vkd->device(), m_view, nullptr);
}
}

151
src/dxvk/dxvk_image.h Normal file
View File

@ -0,0 +1,151 @@
#pragma once
#include "dxvk_memory.h"
#include "dxvk_resource.h"
namespace dxvk {
struct DxvkImageCreateInfo {
VkImageType type;
VkFormat format;
VkSampleCountFlagBits sampleCount;
VkExtent3D extent;
uint32_t numLayers;
uint32_t mipLevels;
VkImageUsageFlags usage;
VkImageTiling tiling;
};
struct DxvkImageViewCreateInfo {
VkImageViewType type;
VkFormat format;
VkImageAspectFlags aspect;
uint32_t minLevel;
uint32_t numLevels;
uint32_t minLayer;
uint32_t numLayers;
};
/**
* \brief DXVK image
*/
class DxvkImage : public DxvkResource {
public:
/**
* \brief Creates a new image
*
* \param [in] vkd Vulkan device functions
* \param [in] info Image properties
* \param [in] memory Image memory
*/
// DxvkImage(
// const Rc<vk::DeviceFn>& vkd,
// const DxvkImageCreateInfo& info,
// DxvkMemory&& memory);
/**
* \brief Creates image object from existing image
*
* This can be used to create an image object for
* an implementation-managed image. Make sure to
* provide the correct image properties, since
* otherwise some image operations may fail.
*/
DxvkImage(
const Rc<vk::DeviceFn>& vkd,
const DxvkImageCreateInfo& info,
VkImage image);
/**
* \brief Destroys image
*
* If this is an implementation-managed image,
* this will not destroy the Vulkan image.
*/
~DxvkImage();
/**
* \brief Image handle
*
* Internal use only.
* \returns Image handle
*/
VkImage handle() const {
return m_image;
}
/**
* \brief Image properties
*
* The image create info structure.
* \returns Image properties
*/
const DxvkImageCreateInfo& info() const {
return m_info;
}
private:
Rc<vk::DeviceFn> m_vkd;
DxvkImageCreateInfo m_info;
DxvkMemory m_memory;
VkImage m_image;
};
/**
* \brief DXVK image view
*/
class DxvkImageView : public DxvkResource {
public:
DxvkImageView(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkImage>& image,
const DxvkImageViewCreateInfo& info);
~DxvkImageView();
/**
* \brief Image view handle
*
* Internal use only.
* \returns Image view handle
*/
VkImageView handle() const {
return m_view;
}
/**
* \brief Image view properties
* \returns Image view properties
*/
const DxvkImageViewCreateInfo& info() const {
return m_info;
}
/**
* \brief Image properties
* \returns Image properties
*/
const DxvkImageCreateInfo& imageInfo() const {
return m_image->info();
}
private:
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkImage> m_image;
DxvkImageViewCreateInfo m_info;
VkImageView m_view;
};
}

10
src/dxvk/dxvk_include.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "../util/util_error.h"
#include "../util/util_string.h"
#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"
#include "./vulkan/dxvk_vulkan_extensions.h"
#include "./vulkan/dxvk_vulkan_loader.h"

108
src/dxvk/dxvk_instance.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "dxvk_instance.h"
#include "dxvk_main.h"
namespace dxvk {
DxvkInstance::DxvkInstance()
: m_vkl(new vk::LibraryFn()),
m_vki(new vk::InstanceFn(this->createInstance())) {
}
DxvkInstance::~DxvkInstance() {
m_vki->vkDestroyInstance(
m_vki->instance(), nullptr);
}
std::vector<Rc<DxvkAdapter>> DxvkInstance::enumAdapters() {
uint32_t numAdapters = 0;
if (m_vki->vkEnumeratePhysicalDevices(m_vki->instance(), &numAdapters, nullptr) != VK_SUCCESS)
throw DxvkError("DxvkInstance::enumAdapters: Failed to enumerate adapters");
std::vector<VkPhysicalDevice> adapters(numAdapters);
if (m_vki->vkEnumeratePhysicalDevices(m_vki->instance(), &numAdapters, adapters.data()) != VK_SUCCESS)
throw DxvkError("DxvkInstance::enumAdapters: Failed to enumerate adapters");
std::vector<Rc<DxvkAdapter>> result;
for (uint32_t i = 0; i < numAdapters; i++)
result.push_back(new DxvkAdapter(this, adapters[i]));
return result;
}
VkInstance DxvkInstance::createInstance() {
auto enabledLayers = this->getLayers();
auto enabledExtensions = this->getExtensions(enabledLayers);
VkApplicationInfo appInfo;
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr;
appInfo.pApplicationName = nullptr;
appInfo.applicationVersion = 0;
appInfo.pEngineName = "DXVK";
appInfo.engineVersion = VK_MAKE_VERSION(0, 0, 1);
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 47);
VkInstanceCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.pApplicationInfo = &appInfo;
info.enabledLayerCount = enabledLayers.count();
info.ppEnabledLayerNames = enabledLayers.names();
info.enabledExtensionCount = enabledExtensions.count();
info.ppEnabledExtensionNames = enabledExtensions.names();
VkInstance result = VK_NULL_HANDLE;
if (m_vkl->vkCreateInstance(&info, nullptr, &result) != VK_SUCCESS)
throw DxvkError("DxvkInstance::createInstance: Failed to create Vulkan instance");
return result;
}
vk::NameList DxvkInstance::getLayers() {
std::vector<const char*> layers = {
"VK_LAYER_LUNARG_standard_validation"
};
const vk::NameSet layersAvailable
= vk::NameSet::enumerateInstanceLayers(*m_vkl);
vk::NameList layersEnabled;
for (auto l : layers) {
if (layersAvailable.supports(l))
layersEnabled.add(l);
}
return layersEnabled;
}
vk::NameList DxvkInstance::getExtensions(const vk::NameList& layers) {
std::vector<const char*> extOptional = { };
std::vector<const char*> extRequired = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
};
const vk::NameSet extensionsAvailable
= vk::NameSet::enumerateInstanceExtensions(*m_vkl, layers);
vk::NameList extensionsEnabled;
for (auto e : extOptional) {
if (extensionsAvailable.supports(e))
extensionsEnabled.add(e);
}
for (auto e : extRequired) {
if (!extensionsAvailable.supports(e))
throw DxvkError(str::format("DxvkInstance::getExtensions: Extension ", e, " not supported"));
extensionsEnabled.add(e);
}
return extensionsEnabled;
}
}

56
src/dxvk/dxvk_instance.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include "dxvk_adapter.h"
#include "dxvk_device.h"
namespace dxvk {
/**
* \brief DXVK instance
*
* Manages a Vulkan instance and stores a list
* of adapters. This also provides methods for
* device creation.
*/
class DxvkInstance : public RcObject {
public:
DxvkInstance();
~DxvkInstance();
/**
* \brief Vulkan instance functions
* \returns Vulkan instance functions
*/
Rc<vk::InstanceFn> vki() const {
return m_vki;
}
/**
* \brief Vulkan instance handle
* \returns The instance handle
*/
VkInstance handle() {
return m_vki->instance();
}
/**
* \brief Retrieves a list of adapters
* \returns List of adapter objects
*/
std::vector<Rc<DxvkAdapter>> enumAdapters();
private:
Rc<vk::LibraryFn> m_vkl;
Rc<vk::InstanceFn> m_vki;
VkInstance createInstance();
vk::NameList getLayers();
vk::NameList getExtensions(const vk::NameList& layers);
};
}

View File

@ -0,0 +1,21 @@
#include "dxvk_lifetime.h"
namespace dxvk {
DxvkLifetimeTracker:: DxvkLifetimeTracker() { }
DxvkLifetimeTracker::~DxvkLifetimeTracker() { }
void DxvkLifetimeTracker::trackResource(const Rc<DxvkResource>& rc) {
if (m_resources.insert(rc).second)
rc->incUseCount();
}
void DxvkLifetimeTracker::reset() {
for (auto i = m_resources.cbegin(); i != m_resources.cend(); i++)
(*i)->decUseCount();
m_resources.clear();
}
}

45
src/dxvk/dxvk_lifetime.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <unordered_set>
#include "dxvk_resource.h"
namespace dxvk {
/**
* \brief DXVK lifetime tracker
*
* Maintains references to a set of resources. This is
* used to guarantee that resources are not destroyed
* or otherwise accessed in an unsafe manner until the
* device has finished using them.
*/
class DxvkLifetimeTracker {
public:
DxvkLifetimeTracker();
~DxvkLifetimeTracker();
/**
* \brief Adds a resource to track
* \param [in] rc The resource to track
*/
void trackResource(
const Rc<DxvkResource>& rc);
/**
* \brief Resets the command list
*
* Called automatically by the device when
* the command list has completed execution.
*/
void reset();
private:
std::unordered_set<Rc<DxvkResource>, RcHash<DxvkResource>> m_resources;
};
}

11
src/dxvk/dxvk_main.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "dxvk_main.h"
namespace dxvk {
Log g_logger("dxvk.log");
void log(const std::string& message) {
g_logger.log(message);
}
}

14
src/dxvk/dxvk_main.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include "../util/log/log.h"
namespace dxvk {
/**
* \brief Adds a message to the global DXVK log
* \param [in] message Log message
*/
void log(const std::string& message);
}

112
src/dxvk/dxvk_memory.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "dxvk_memory.h"
namespace dxvk {
DxvkMemory::DxvkMemory() {
}
DxvkMemory::DxvkMemory(
DxvkMemoryAllocator* alloc,
VkDeviceMemory memory,
void* mapPtr)
: m_alloc (alloc),
m_memory(memory),
m_mapPtr(mapPtr) { }
DxvkMemory::DxvkMemory(DxvkMemory&& other)
: m_alloc (other.m_alloc),
m_memory(other.m_memory),
m_mapPtr(other.m_mapPtr) {
other.m_alloc = nullptr;
other.m_memory = VK_NULL_HANDLE;
other.m_mapPtr = nullptr;
}
DxvkMemory& DxvkMemory::operator = (DxvkMemory&& other) {
this->m_alloc = other.m_alloc;
this->m_memory = other.m_memory;
this->m_mapPtr = other.m_mapPtr;
other.m_alloc = nullptr;
other.m_memory = VK_NULL_HANDLE;
other.m_mapPtr = nullptr;
return *this;
}
DxvkMemory::~DxvkMemory() {
if (m_memory != VK_NULL_HANDLE)
m_alloc->freeMemory(m_memory);
}
DxvkMemoryAllocator::DxvkMemoryAllocator(
const Rc<DxvkAdapter>& adapter,
const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd), m_memProps(adapter->memoryProperties()) {
}
DxvkMemoryAllocator::~DxvkMemoryAllocator() {
}
DxvkMemory DxvkMemoryAllocator::alloc(
const VkMemoryRequirements& req,
const VkMemoryPropertyFlags flags) {
VkDeviceMemory memory = VK_NULL_HANDLE;
void* mapPtr = nullptr;
for (uint32_t i = 0; i < m_memProps.memoryTypeCount; i++) {
const bool supported = (req.memoryTypeBits & (1u << i)) != 0;
const bool adequate = (m_memProps.memoryTypes[i].propertyFlags & flags) == flags;
if (supported && adequate) {
memory = this->allocMemory(req.size, i);
if (memory != VK_NULL_HANDLE)
break;
}
}
if (memory == VK_NULL_HANDLE)
throw DxvkError("DxvkMemoryAllocator::alloc: Failed to allocate memory");
if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
if (m_vkd->vkMapMemory(m_vkd->device(), memory,
0, VK_WHOLE_SIZE, 0, &mapPtr) != VK_SUCCESS)
throw DxvkError("DxvkMemoryAllocator::alloc: Failed to map memory");
}
return DxvkMemory(this, memory, mapPtr);
}
VkDeviceMemory DxvkMemoryAllocator::allocMemory(
VkDeviceSize blockSize, uint32_t memoryType) {
VkMemoryAllocateInfo info;
info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
info.pNext = nullptr;
info.allocationSize = blockSize;
info.memoryTypeIndex = memoryType;
VkDeviceMemory memory = VK_NULL_HANDLE;
if (m_vkd->vkAllocateMemory(m_vkd->device(),
&info, nullptr, &memory) != VK_SUCCESS)
return VK_NULL_HANDLE;
return memory;
}
void DxvkMemoryAllocator::freeMemory(VkDeviceMemory memory) {
m_vkd->vkFreeMemory(m_vkd->device(), memory, nullptr);
}
}

105
src/dxvk/dxvk_memory.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
#include "dxvk_adapter.h"
namespace dxvk {
class DxvkMemoryAllocator;
/**
* \brief Memory slice
*/
class DxvkMemory {
public:
DxvkMemory();
DxvkMemory(
DxvkMemoryAllocator* alloc,
VkDeviceMemory memory,
void* mapPtr);
DxvkMemory (DxvkMemory&& other);
DxvkMemory& operator = (DxvkMemory&& other);
~DxvkMemory();
/**
* \brief Memory object
*
* This information is required when
* binding memory to Vulkan objects.
* \returns Memory object
*/
VkDeviceMemory memory() const {
return m_memory;
}
/**
* \brief Offset from memory object
*
* This information is required when
* binding memory to Vulkan objects.
* \returns Offset from memory object
*/
VkDeviceSize offset() const {
return 0;
}
/**
* \brief Pointer to mapped data
* \returns Pointer to mapped data
*/
void* mapPtr() const {
return m_mapPtr;
}
private:
DxvkMemoryAllocator* m_alloc = nullptr;
VkDeviceMemory m_memory = VK_NULL_HANDLE;
void* m_mapPtr = nullptr;
};
/**
* \brief Memory allocator
*
* Allocates device memory for Vulkan resources.
* Memory objects will be destroyed automatically.
*/
class DxvkMemoryAllocator {
friend class DxvkMemory;
public:
DxvkMemoryAllocator(
const Rc<DxvkAdapter>& adapter,
const Rc<vk::DeviceFn>& vkd);
~DxvkMemoryAllocator();
/**
* \brief Allocates device memory
*
* \param [in] req Memory requirements
* \param [in] flats Memory type flags
* \returns Allocated memory slice
*/
DxvkMemory alloc(
const VkMemoryRequirements& req,
const VkMemoryPropertyFlags flags);
private:
const Rc<vk::DeviceFn> m_vkd;
const VkPhysicalDeviceMemoryProperties m_memProps;
VkDeviceMemory allocMemory(
VkDeviceSize blockSize,
uint32_t memoryType);
void freeMemory(
VkDeviceMemory memory);
};
}

View File

@ -0,0 +1,166 @@
#include "dxvk_renderpass.h"
namespace dxvk {
DxvkRenderPassFormat::DxvkRenderPassFormat() {
for (uint32_t i = 0; i < MaxNumColorTargets; i++)
m_color.at(i) = VK_FORMAT_UNDEFINED;
m_depth = VK_FORMAT_UNDEFINED;
m_samples = VK_SAMPLE_COUNT_1_BIT;
}
size_t DxvkRenderPassFormat::hash() const {
DxvkHashState result;
std::hash<VkFormat> fhash;
std::hash<VkSampleCountFlagBits> shash;
for (uint32_t i = 0; i < MaxNumColorTargets; i++)
result.add(fhash(m_color.at(i)));
result.add(fhash(m_depth));
result.add(shash(m_samples));
return result;
}
bool DxvkRenderPassFormat::operator == (const DxvkRenderPassFormat& other) const {
bool equal = m_depth == other.m_depth
&& m_samples == other.m_samples;
for (uint32_t i = 0; i < MaxNumColorTargets && !equal; i++)
equal = m_color.at(i) == other.m_color.at(i);
return equal;
}
bool DxvkRenderPassFormat::operator != (const DxvkRenderPassFormat& other) const {
return !this->operator == (other);
}
DxvkRenderPass::DxvkRenderPass(
const Rc<vk::DeviceFn>& vkd,
const DxvkRenderPassFormat& fmt,
VkImageLayout initialLayout,
VkImageLayout finalLayout)
: m_vkd(vkd), m_format(fmt) {
std::vector<VkAttachmentDescription> attachments;
VkAttachmentReference depthRef;
std::array<VkAttachmentReference, MaxNumColorTargets> colorRef;
// Render passes may not require the previous
// contents of the attachments to be preserved.
VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
VkAttachmentStoreOp storeOp = VK_ATTACHMENT_STORE_OP_STORE;
if (initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
if (fmt.getDepthFormat() != VK_FORMAT_UNDEFINED) {
VkAttachmentDescription desc;
desc.flags = 0;
desc.format = fmt.getDepthFormat();
desc.samples = fmt.getSampleCount();
desc.loadOp = loadOp;
desc.storeOp = storeOp;
desc.stencilLoadOp = loadOp;
desc.stencilStoreOp = storeOp;
desc.initialLayout = initialLayout;
desc.finalLayout = finalLayout;
depthRef.attachment = attachments.size();
depthRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments.push_back(desc);
}
for (uint32_t i = 0; i < MaxNumColorTargets; i++) {
colorRef.at(i).attachment = VK_ATTACHMENT_UNUSED;
colorRef.at(i).layout = VK_IMAGE_LAYOUT_UNDEFINED;
if (fmt.getColorFormat(i) != VK_FORMAT_UNDEFINED) {
VkAttachmentDescription desc;
desc.flags = 0;
desc.format = fmt.getColorFormat(i);
desc.samples = fmt.getSampleCount();
desc.loadOp = loadOp;
desc.storeOp = storeOp;
desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc.initialLayout = initialLayout;
desc.finalLayout = finalLayout;
colorRef.at(i).attachment = attachments.size();
colorRef.at(i).layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(desc);
}
}
VkSubpassDescription subpass;
subpass.flags = 0;
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = nullptr;
subpass.colorAttachmentCount = colorRef.size();
subpass.pColorAttachments = colorRef.data();
subpass.pResolveAttachments = nullptr;
subpass.pDepthStencilAttachment = fmt.getDepthFormat() != VK_FORMAT_UNDEFINED ? &depthRef : nullptr;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;
VkRenderPassCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.attachmentCount = attachments.size();
info.pAttachments = attachments.data();
info.subpassCount = 1;
info.pSubpasses = &subpass;
info.dependencyCount = 0;
info.pDependencies = nullptr;
if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &m_renderPass) != VK_SUCCESS)
throw DxvkError("DxvkRenderPass::DxvkRenderPass: Failed to create render pass object");
}
DxvkRenderPass::~DxvkRenderPass() {
m_vkd->vkDestroyRenderPass(
m_vkd->device(), m_renderPass, nullptr);
}
DxvkRenderPassPool::DxvkRenderPassPool(const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) { }
DxvkRenderPassPool::~DxvkRenderPassPool() {
}
Rc<DxvkRenderPass> DxvkRenderPassPool::getRenderPass(
const DxvkRenderPassFormat& fmt) {
std::lock_guard<std::mutex> lock(m_mutex);
auto rp = m_renderPasses.find(fmt);
if (rp != m_renderPasses.end())
return rp->second;
auto result = this->createRenderPass(fmt);
m_renderPasses.insert(std::make_pair(fmt, result));
return result;
}
Rc<DxvkRenderPass> DxvkRenderPassPool::createRenderPass(
const DxvkRenderPassFormat& fmt) {
return new DxvkRenderPass(m_vkd, fmt,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_GENERAL);
}
}

175
src/dxvk/dxvk_renderpass.h Normal file
View File

@ -0,0 +1,175 @@
#pragma once
#include <mutex>
#include <unordered_map>
#include "dxvk_hash.h"
#include "dxvk_include.h"
namespace dxvk {
constexpr uint32_t MaxNumColorTargets = 8;
/**
* \brief Render pass format
*
* Stores the formats of all render targets
* that are used by a framebuffer object.
*/
class DxvkRenderPassFormat {
public:
DxvkRenderPassFormat();
/**
* \brief Retrieves color target format
*
* If the color target has not been defined,
* this will return \c VK_FORMAT_UNDEFINED.
* \param [in] id Color target index
* \returns Color target format
*/
VkFormat getColorFormat(uint32_t id) const {
return m_color.at(id);
}
/**
* \brief Retrieves depth-stencil format
*
* If the color target has not been defined,
* this will return \c VK_FORMAT_UNDEFINED.
*/
VkFormat getDepthFormat() const {
return m_depth;
}
/**
* \brief Retrieves sample count
*
* If no sample count has been explicitly specitied,
* this will return \c VK_SAMPLE_COUNT_1_BIT.
* \returns Sample count
*/
VkSampleCountFlagBits getSampleCount() const {
return m_samples;
}
/**
* \brief Sets color target format
*
* \param [in] id Color target index
* \param [in] fmt Color target format
*/
void setColorFormat(uint32_t id, VkFormat fmt) {
m_color.at(id) = fmt;
}
/**
* \brief Sets depth-stencil format
* \param [in] fmt Depth-stencil format
*/
void setDepthFormat(VkFormat fmt) {
m_depth = fmt;
}
/**
* \brief Sets sample count
* \param [in] samples Sample count
*/
void setSampleCount(VkSampleCountFlagBits samples) {
m_samples = samples;
}
/**
* \brief Computes the hash
* \returns Resulting hash
*/
size_t hash() const;
bool operator == (const DxvkRenderPassFormat& other) const;
bool operator != (const DxvkRenderPassFormat& other) const;
private:
std::array<VkFormat, MaxNumColorTargets> m_color;
VkFormat m_depth;
VkSampleCountFlagBits m_samples;
};
/**
* \brief DXVK render pass
*
* Render pass objects are used internally to identify render
* target formats and
*/
class DxvkRenderPass : public RcObject {
public:
DxvkRenderPass(
const Rc<vk::DeviceFn>& vkd,
const DxvkRenderPassFormat& fmt,
VkImageLayout initialLayout,
VkImageLayout finalLayout);
~DxvkRenderPass();
/**
* \brief Render pass handle
*
* Internal use only.
* \returns Render pass handle
*/
VkRenderPass handle() const {
return m_renderPass;
}
private:
Rc<vk::DeviceFn> m_vkd;
DxvkRenderPassFormat m_format;
VkRenderPass m_renderPass;
};
/**
* \brief Render pass pool
*
* Thread-safe class that manages the render pass
* objects that are used within an application.
*/
class DxvkRenderPassPool {
public:
DxvkRenderPassPool(const Rc<vk::DeviceFn>& vkd);
~DxvkRenderPassPool();
/**
* \brief Retrieves a render pass object
*
* \param [in] fmt Render target formats
* \returns Compatible render pass object
*/
Rc<DxvkRenderPass> getRenderPass(
const DxvkRenderPassFormat& fmt);
private:
Rc<vk::DeviceFn> m_vkd;
std::mutex m_mutex;
std::unordered_map<
DxvkRenderPassFormat,
Rc<DxvkRenderPass>,
DxvkHash> m_renderPasses;
Rc<DxvkRenderPass> createRenderPass(
const DxvkRenderPassFormat& fmt);
};
}

View File

@ -0,0 +1,9 @@
#include "dxvk_resource.h"
namespace dxvk {
DxvkResource::~DxvkResource() {
}
}

33
src/dxvk/dxvk_resource.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "dxvk_include.h"
namespace dxvk {
/**
* \brief DXVK resource
*
* Keeps track of whether the resource is currently in use
* by the GPU. As soon as a command that uses the resource
* is recorded, it will be marked as 'in use'.
*/
class DxvkResource : public RcObject {
public:
virtual ~DxvkResource();
bool isInUse() const {
return m_useCount != 0;
}
void incUseCount() { m_useCount += 1; }
void decUseCount() { m_useCount -= 1; }
private:
std::atomic<uint32_t> m_useCount = { 0u };
};
}

150
src/dxvk/dxvk_surface.cpp Normal file
View File

@ -0,0 +1,150 @@
#include "dxvk_surface.h"
#include "../util/util_math.h"
namespace dxvk {
DxvkSurface::DxvkSurface(
const Rc<DxvkAdapter>& adapter,
HINSTANCE instance,
HWND window)
: m_adapter (adapter),
m_vki (adapter->vki()),
m_handle (createSurface(instance, window)),
m_surfaceFormats (getSurfaceFormats()),
m_presentModes (getPresentModes()) {
}
DxvkSurface::~DxvkSurface() {
m_vki->vkDestroySurfaceKHR(
m_vki->instance(), m_handle, nullptr);
}
VkSurfaceCapabilitiesKHR DxvkSurface::getSurfaceCapabilities() const {
VkSurfaceCapabilitiesKHR surfaceCaps;
if (m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
m_adapter->handle(), m_handle, &surfaceCaps) != VK_SUCCESS)
throw DxvkError("DxvkSurface::getSurfaceCapabilities: Failed to query surface capabilities");
return surfaceCaps;
}
VkSurfaceFormatKHR DxvkSurface::pickSurfaceFormat(VkSurfaceFormatKHR preferred) const {
// If the implementation allows us to freely choose
// the format, we'll just use the preferred format.
if (m_surfaceFormats.size() == 1 && m_surfaceFormats.at(0).format == VK_FORMAT_UNDEFINED)
return preferred;
// If the preferred format is explicitly listed in
// the array of supported surface formats, use it
for (auto fmt : m_surfaceFormats) {
if (fmt.format == preferred.format
&& fmt.colorSpace == preferred.colorSpace)
return fmt;
}
// Otherwise, fall back to the first format
return m_surfaceFormats.at(0);
}
VkPresentModeKHR DxvkSurface::pickPresentMode(VkPresentModeKHR preferred) const {
for (auto mode : m_presentModes) {
if (mode == preferred)
return mode;
}
// This mode is guaranteed to be available
return VK_PRESENT_MODE_FIFO_KHR;
}
uint32_t DxvkSurface::pickImageCount(
const VkSurfaceCapabilitiesKHR& caps,
VkPresentModeKHR mode) const {
uint32_t count = caps.minImageCount;
if (mode == VK_PRESENT_MODE_MAILBOX_KHR
|| mode == VK_PRESENT_MODE_FIFO_KHR)
count += 1;
if (count > caps.maxImageCount && caps.maxImageCount != 0)
count = caps.maxImageCount;
return count;
}
VkExtent2D DxvkSurface::pickImageExtent(
const VkSurfaceCapabilitiesKHR& caps,
VkExtent2D preferred) const {
if (caps.currentExtent.width != std::numeric_limits<uint32_t>::max())
return caps.currentExtent;
VkExtent2D actual;
actual.width = clamp(preferred.width, caps.minImageExtent.width, caps.maxImageExtent.width);
actual.height = clamp(preferred.height, caps.minImageExtent.height, caps.maxImageExtent.height);
return actual;
}
VkSurfaceKHR DxvkSurface::createSurface(HINSTANCE instance, HWND window) {
VkWin32SurfaceCreateInfoKHR info;
info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
info.pNext = nullptr;
info.flags = 0;
info.hinstance = instance;
info.hwnd = window;
VkSurfaceKHR surface = VK_NULL_HANDLE;
if (m_vki->vkCreateWin32SurfaceKHR(m_vki->instance(), &info, nullptr, &surface) != VK_SUCCESS)
throw DxvkError("DxvkSurface::createSurface: Failed to create win32 surface");
VkBool32 supportStatus = VK_FALSE;
if (m_vki->vkGetPhysicalDeviceSurfaceSupportKHR(m_adapter->handle(),
m_adapter->presentQueueFamily(), surface, &supportStatus) != VK_SUCCESS) {
m_vki->vkDestroySurfaceKHR(m_vki->instance(), surface, nullptr);
throw DxvkError("DxvkSurface::createSurface: Failed to query surface support");
}
if (!supportStatus) {
m_vki->vkDestroySurfaceKHR(m_vki->instance(), surface, nullptr);
throw DxvkError("DxvkSurface::createSurface: Surface not supported by device");
}
return surface;
}
std::vector<VkSurfaceFormatKHR> DxvkSurface::getSurfaceFormats() {
uint32_t numFormats = 0;
if (m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(
m_adapter->handle(), m_handle, &numFormats, nullptr) != VK_SUCCESS)
throw DxvkError("DxvkSurface::getSurfaceFormats: Failed to query surface formats");
std::vector<VkSurfaceFormatKHR> formats(numFormats);
if (m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(
m_adapter->handle(), m_handle, &numFormats, formats.data()) != VK_SUCCESS)
throw DxvkError("DxvkSurface::getSurfaceFormats: Failed to query surface formats");
return formats;
}
std::vector<VkPresentModeKHR> DxvkSurface::getPresentModes() {
uint32_t numModes = 0;
if (m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(
m_adapter->handle(), m_handle, &numModes, nullptr) != VK_SUCCESS)
throw DxvkError("DxvkSurface::getPresentModes: Failed to query present modes");
std::vector<VkPresentModeKHR> modes(numModes);
if (m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(
m_adapter->handle(), m_handle, &numModes, modes.data()) != VK_SUCCESS)
throw DxvkError("DxvkSurface::getPresentModes: Failed to query present modes");
return modes;
}
}

98
src/dxvk/dxvk_surface.h Normal file
View File

@ -0,0 +1,98 @@
#pragma once
#include "dxvk_adapter.h"
namespace dxvk {
/**
* \brief DXVK surface
*
* Vulkan representation of a drawable window surface. This
* class provides methods to query the current dimension of
* the surface as well as format support queries.
*/
class DxvkSurface : public RcObject {
public:
DxvkSurface(
const Rc<DxvkAdapter>& adapter,
HINSTANCE instance,
HWND window);
~DxvkSurface();
/**
* \brief Vulkan surface handle
* \returns The surface handle
*/
VkSurfaceKHR handle() const {
return m_handle;
}
/**
* \brief Queries surface capabilities
*
* Retrieves up-to-date information about the surface,
* such as the bounds of the swapchain images.
* \returns Current surface properties
*/
VkSurfaceCapabilitiesKHR getSurfaceCapabilities() const;
/**
* \brief Picks a suitable surface format
*
* \param [in] preferred Preferred surface format
* \returns The actual surface format
*/
VkSurfaceFormatKHR pickSurfaceFormat(
VkSurfaceFormatKHR preferred) const;
/**
* \brief Picks a supported present mode
*
* \param [in] preferred The preferred present mode
* \returns The actual present mode
*/
VkPresentModeKHR pickPresentMode(
VkPresentModeKHR preferred) const;
/**
* \brief Picks a suitable image count for a swap chain
*
* \param [in] caps Surface capabilities
* \param [in] mode The present mode
* \returns Suitable image count
*/
uint32_t pickImageCount(
const VkSurfaceCapabilitiesKHR& caps,
VkPresentModeKHR mode) const;
/**
* \brief Picks a suitable image size for a swap chain
*
* \param [in] caps Surface capabilities
* \param [in] preferred Preferred image size
* \returns Selected image size
*/
VkExtent2D pickImageExtent(
const VkSurfaceCapabilitiesKHR& caps,
VkExtent2D preferred) const;
private:
Rc<DxvkAdapter> m_adapter;
Rc<vk::InstanceFn> m_vki;
VkSurfaceKHR m_handle;
std::vector<VkSurfaceFormatKHR> m_surfaceFormats;
std::vector<VkPresentModeKHR> m_presentModes;
VkSurfaceKHR createSurface(HINSTANCE instance, HWND window);
std::vector<VkSurfaceFormatKHR> getSurfaceFormats();
std::vector<VkPresentModeKHR> getPresentModes();
};
}

185
src/dxvk/dxvk_swapchain.cpp Normal file
View File

@ -0,0 +1,185 @@
#include "dxvk_main.h"
#include "dxvk_device.h"
#include "dxvk_framebuffer.h"
#include "dxvk_surface.h"
#include "dxvk_swapchain.h"
namespace dxvk {
DxvkSwapchain::DxvkSwapchain(
const Rc<DxvkDevice>& device,
const Rc<DxvkSurface>& surface,
const DxvkSwapchainProperties& properties,
VkQueue queue)
: m_device (device),
m_vkd (device->vkd()),
m_surface (surface),
m_queue (queue),
m_properties(properties) {
this->recreateSwapchain();
}
DxvkSwapchain::~DxvkSwapchain() {
m_device->waitForIdle();
m_vkd->vkDestroySwapchainKHR(
m_vkd->device(), m_handle, nullptr);
}
Rc<DxvkFramebuffer> DxvkSwapchain::getFramebuffer(
const Rc<DxvkSemaphore>& wakeSync) {
VkResult status = this->acquireNextImage(wakeSync);
if (status == VK_ERROR_OUT_OF_DATE_KHR) {
this->recreateSwapchain();
status = this->acquireNextImage(wakeSync);
}
if (status != VK_SUCCESS
&& status != VK_SUBOPTIMAL_KHR)
throw DxvkError("DxvkSwapchain::getFramebuffer: Failed to acquire image");
return m_framebuffers.at(m_imageIndex);
}
void DxvkSwapchain::present(const Rc<DxvkSemaphore>& waitSync) {
const VkSemaphore waitSemaphore = waitSync->handle();
VkPresentInfoKHR info;
info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
info.pNext = nullptr;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &waitSemaphore;
info.swapchainCount = 1;
info.pSwapchains = &m_handle;
info.pImageIndices = &m_imageIndex;
info.pResults = nullptr;
VkResult status = m_vkd->vkQueuePresentKHR(m_queue, &info);
if (status != VK_SUCCESS
&& status != VK_SUBOPTIMAL_KHR
&& status != VK_ERROR_OUT_OF_DATE_KHR)
throw DxvkError("DxvkSwapchain::present: Failed to present image");
}
void DxvkSwapchain::changeProperties(
const DxvkSwapchainProperties& props) {
m_properties = props;
this->recreateSwapchain();
}
VkResult DxvkSwapchain::acquireNextImage(
const Rc<DxvkSemaphore>& wakeSync) {
return m_vkd->vkAcquireNextImageKHR(
m_vkd->device(), m_handle,
std::numeric_limits<uint64_t>::max(),
wakeSync->handle(),
VK_NULL_HANDLE,
&m_imageIndex);
}
void DxvkSwapchain::recreateSwapchain() {
VkSwapchainKHR oldSwapchain = m_handle;
// Wait until we can be certain that none of our
// resources are still in use by the device.
m_device->waitForIdle();
// Recreate the actual swapchain object
auto caps = m_surface->getSurfaceCapabilities();
auto fmt = m_surface->pickSurfaceFormat(m_properties.preferredSurfaceFormat);
auto mode = m_surface->pickPresentMode (m_properties.preferredPresentMode);
VkSwapchainCreateInfoKHR swapInfo;
swapInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapInfo.pNext = nullptr;
swapInfo.flags = 0;
swapInfo.surface = m_surface->handle();
swapInfo.minImageCount = m_surface->pickImageCount(caps, mode);
swapInfo.imageFormat = fmt.format;
swapInfo.imageColorSpace = fmt.colorSpace;
swapInfo.imageExtent = m_surface->pickImageExtent(caps, m_properties.preferredBufferSize);
swapInfo.imageArrayLayers = 1;
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapInfo.queueFamilyIndexCount = 0;
swapInfo.pQueueFamilyIndices = nullptr;
swapInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapInfo.presentMode = mode;
swapInfo.clipped = VK_TRUE;
swapInfo.oldSwapchain = oldSwapchain;
if (m_vkd->vkCreateSwapchainKHR(m_vkd->device(), &swapInfo, nullptr, &m_handle) != VK_SUCCESS)
throw DxvkError("DxvkSwapchain::recreateSwapchain: Failed to recreate swap chain");
// Destroy previous swapchain object
m_vkd->vkDestroySwapchainKHR(
m_vkd->device(), oldSwapchain, nullptr);
// Create the render pass object
DxvkRenderPassFormat renderTargetFormat;
renderTargetFormat.setColorFormat(0, fmt.format);
m_renderPass = new DxvkRenderPass(
m_vkd, renderTargetFormat,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
// Retrieve swap images
auto swapImages = this->retrieveSwapImages();
m_framebuffers.resize(swapImages.size());
for (size_t i = 0; i < swapImages.size(); i++) {
DxvkImageCreateInfo imageInfo;
imageInfo.type = VK_IMAGE_TYPE_2D;
imageInfo.format = fmt.format;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent.width = swapInfo.imageExtent.width;
imageInfo.extent.height = swapInfo.imageExtent.height;
imageInfo.extent.depth = 1;
imageInfo.numLayers = swapInfo.imageArrayLayers;
imageInfo.mipLevels = 1;
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
DxvkImageViewCreateInfo viewInfo;
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = fmt.format;
viewInfo.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = swapInfo.imageArrayLayers;
Rc<DxvkImage> image = new DxvkImage(m_vkd, imageInfo, swapImages.at(i));
Rc<DxvkImageView> iview = m_device->createImageView(image, viewInfo);
DxvkRenderTargets renderTargets;
renderTargets.setColorTarget(0, iview);
m_framebuffers.at(i) = new DxvkFramebuffer(
m_vkd, m_renderPass, renderTargets);
}
}
std::vector<VkImage> DxvkSwapchain::retrieveSwapImages() {
uint32_t imageCount = 0;
if (m_vkd->vkGetSwapchainImagesKHR(m_vkd->device(), m_handle, &imageCount, nullptr) != VK_SUCCESS)
throw DxvkError("DxvkSwapchain::recreateSwapchain: Failed to retrieve swap chain images");
std::vector<VkImage> images(imageCount);
if (m_vkd->vkGetSwapchainImagesKHR(m_vkd->device(), m_handle, &imageCount, images.data()) != VK_SUCCESS)
throw DxvkError("DxvkSwapchain::recreateSwapchain: Failed to retrieve swap chain images");
return images;
}
}

96
src/dxvk/dxvk_swapchain.h Normal file
View File

@ -0,0 +1,96 @@
#pragma once
#include "dxvk_framebuffer.h"
#include "dxvk_sync.h"
namespace dxvk {
class DxvkDevice;
class DxvkFramebuffer;
class DxvkSurface;
/**
* \brief
*/
struct DxvkSwapchainProperties {
VkSurfaceFormatKHR preferredSurfaceFormat;
VkPresentModeKHR preferredPresentMode;
VkExtent2D preferredBufferSize;
};
/**
* \brief DXVK swapchain
*
* Manages a Vulkan swapchain object.
*/
class DxvkSwapchain : public RcObject {
public:
DxvkSwapchain(
const Rc<DxvkDevice>& device,
const Rc<DxvkSurface>& surface,
const DxvkSwapchainProperties& properties,
VkQueue queue);
~DxvkSwapchain();
/**
* \brief Retrieves the framebuffer for the current frame
*
* If necessary, this will automatically recreate the
* underlying swapchain object and framebuffer objects.
* \param [in] wakeSync Semaphore to signal
* \returns The framebuffer object
*/
Rc<DxvkFramebuffer> getFramebuffer(
const Rc<DxvkSemaphore>& wakeSync);
/**
* \brief Presents the current framebuffer
*
* This may actually fail to present an image. If that is the
* case, the surface contents will be undefined for this frame
* and the swapchain object will be recreated.
* \param [in] waitSync Semaphore to wait on
*/
void present(
const Rc<DxvkSemaphore>& waitSync);
/**
* \brief Changes swapchain properties
*
* This must not be called between \ref getFramebuffer
* and \ref present as this method may recreate the swap
* chain and framebuffer objects immediately.
* \param [in] props New swapchain properties
*/
void changeProperties(
const DxvkSwapchainProperties& props);
private:
Rc<DxvkDevice> m_device;
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkSurface> m_surface;
VkQueue m_queue;
DxvkSwapchainProperties m_properties;
VkSwapchainKHR m_handle = VK_NULL_HANDLE;
uint32_t m_imageIndex = 0;
uint32_t m_frameIndex = 0;
Rc<DxvkRenderPass> m_renderPass;
std::vector<Rc<DxvkFramebuffer>> m_framebuffers;
VkResult acquireNextImage(
const Rc<DxvkSemaphore>& wakeSync);
void recreateSwapchain();
std::vector<VkImage> retrieveSwapImages();
};
}

57
src/dxvk/dxvk_sync.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "dxvk_sync.h"
namespace dxvk {
DxvkSemaphore::DxvkSemaphore(
const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) {
VkSemaphoreCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
if (m_vkd->vkCreateSemaphore(m_vkd->device(), &info, nullptr, &m_semaphore) != VK_SUCCESS)
throw DxvkError("DxvkSemaphore::DxvkSemaphore: Failed to create semaphore");
}
DxvkSemaphore::~DxvkSemaphore() {
m_vkd->vkDestroySemaphore(
m_vkd->device(), m_semaphore, nullptr);
}
DxvkFence::DxvkFence(const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) {
VkFenceCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
if (m_vkd->vkCreateFence(m_vkd->device(), &info, nullptr, &m_fence) != VK_SUCCESS)
throw DxvkError("DxvkFence::DxvkFence: Failed to create fence");
}
DxvkFence::~DxvkFence() {
m_vkd->vkDestroyFence(
m_vkd->device(), m_fence, nullptr);
}
bool DxvkFence::wait(uint64_t timeout) const {
VkResult status = m_vkd->vkWaitForFences(
m_vkd->device(), 1, &m_fence, VK_FALSE, timeout);
if (status == VK_SUCCESS) return true;
if (status == VK_TIMEOUT) return false;
throw DxvkError("DxvkFence::wait: Failed to wait for fence");
}
void DxvkFence::reset() {
if (m_vkd->vkResetFences(m_vkd->device(), 1, &m_fence) != VK_SUCCESS)
throw DxvkError("DxvkFence::reset: Failed to reset fence");
}
}

89
src/dxvk/dxvk_sync.h Normal file
View File

@ -0,0 +1,89 @@
#pragma once
#include "dxvk_resource.h"
namespace dxvk {
/**
* \brief Semaphore object
*
* This is merely an abstraction of Vulkan's semaphores.
* They are only used internally by \ref DxvkSwapchain
* in order to synchronize the presentation engine with
* command buffer submissions.
*/
class DxvkSemaphore : public DxvkResource {
public:
DxvkSemaphore(const Rc<vk::DeviceFn>& vkd);
~DxvkSemaphore();
/**
* \brief Semaphore handle
*
* Internal use only.
* \returns Semaphore handle
*/
VkSemaphore handle() const {
return m_semaphore;
}
private:
Rc<vk::DeviceFn> m_vkd;
VkSemaphore m_semaphore;
};
/**
* \brief Fence object
*
* This is merely an abstraction of Vulkan's fences. Client
* APIs that support fence operations may use them directly.
* Other than that, they are used internally to keep track
* of GPU resource usage.
*/
class DxvkFence : public RcObject {
public:
DxvkFence(const Rc<vk::DeviceFn>& vkd);
~DxvkFence();
/**
* \brief Fence handle
*
* Internal use only.
* \returns Fence handle
*/
VkFence handle() const {
return m_fence;
}
/**
* \brief Waits for fence to be signaled
*
* \param [in] timeout Amount of time to wait
* \returns \c true if the fence has been signaled,
* \c false if a timeout occured.
*/
bool wait(uint64_t timeout) const;
/**
* \brief Resets the fence
*
* Transitions the fence into the unsignaled state,
* which means that the fence may be submitted again.
*/
void reset();
private:
Rc<vk::DeviceFn> m_vkd;
VkFence m_fence;
};
}

32
src/dxvk/meson.build Normal file
View File

@ -0,0 +1,32 @@
dxvk_src = files([
'dxvk_adapter.cpp',
'dxvk_cmdlist.cpp',
'dxvk_context.cpp',
'dxvk_context_state.cpp',
'dxvk_device.cpp',
'dxvk_framebuffer.cpp',
'dxvk_image.cpp',
'dxvk_instance.cpp',
'dxvk_lifetime.cpp',
'dxvk_main.cpp',
'dxvk_memory.cpp',
'dxvk_renderpass.cpp',
'dxvk_resource.cpp',
'dxvk_surface.cpp',
'dxvk_swapchain.cpp',
'dxvk_sync.cpp',
'vulkan/dxvk_vulkan_extensions.cpp',
'vulkan/dxvk_vulkan_loader.cpp',
])
thread_dep = dependency('threads')
dxvk_lib = static_library('dxvk', dxvk_src,
link_with : [ util_lib ],
dependencies : [ thread_dep, lib_vulkan, lib_sdl2 ],
include_directories : [ dxvk_include_path ])
dxvk_dep = declare_dependency(
link_with : [ dxvk_lib ],
include_directories : [ dxvk_include_path, include_directories('.') ])

View File

@ -0,0 +1,71 @@
#include "dxvk_vulkan_extensions.h"
namespace dxvk::vk {
bool NameSet::supports(const char* name) const {
return m_names.find(name) != m_names.end();
}
NameSet NameSet::enumerateInstanceLayers(const LibraryFn& vkl) {
uint32_t layerCount = 0;
if (vkl.vkEnumerateInstanceLayerProperties(&layerCount, nullptr) != VK_SUCCESS)
throw DxvkError(str::format("LayerSet::enumerateInstanceLayers: Failed to query instance layers"));
std::vector<VkLayerProperties> layers(layerCount);
if (vkl.vkEnumerateInstanceLayerProperties(&layerCount, layers.data()) != VK_SUCCESS)
throw DxvkError(str::format("LayerSet::enumerateInstanceLayers: Failed to query instance layers"));
NameSet result;
for (const auto& layer : layers)
result.m_names.insert(layer.layerName);
return result;
}
NameSet NameSet::enumerateInstanceExtensions(
const LibraryFn& vkl,
const NameList& layers) {
NameSet result;
result.addInstanceLayerExtensions(vkl, nullptr);
for (size_t i = 0; i < layers.count(); i++)
result.addInstanceLayerExtensions(vkl, layers.name(i));
return result;
}
NameSet NameSet::enumerateDeviceExtensions(
const InstanceFn& vki,
VkPhysicalDevice device) {
uint32_t extCount = 0;
if (vki.vkEnumerateDeviceExtensionProperties(device, nullptr, &extCount, nullptr) != VK_SUCCESS)
throw DxvkError("ExtensionSet::addDeviceExtensions: Failed to query device extensions");
std::vector<VkExtensionProperties> extensions(extCount);
if (vki.vkEnumerateDeviceExtensionProperties(device, nullptr, &extCount, extensions.data()) != VK_SUCCESS)
throw DxvkError("ExtensionSet::addDeviceExtensions: Failed to query device extensions");
NameSet result;
for (const auto& ext : extensions)
result.m_names.insert(ext.extensionName);
return result;
}
void NameSet::addInstanceLayerExtensions(
const LibraryFn& vkl,
const char* layer) {
uint32_t extCount = 0;
if (vkl.vkEnumerateInstanceExtensionProperties(layer, &extCount, nullptr) != VK_SUCCESS)
throw DxvkError("ExtensionSet::addInstanceExtensions: Failed to query instance extensions");
std::vector<VkExtensionProperties> extensions(extCount);
if (vkl.vkEnumerateInstanceExtensionProperties(layer, &extCount, extensions.data()) != VK_SUCCESS)
throw DxvkError("ExtensionSet::addInstanceExtensions: Failed to query instance extensions");
for (const auto& ext : extensions)
m_names.insert(ext.extensionName);
}
}

View File

@ -0,0 +1,103 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_set>
#include "dxvk_vulkan_loader.h"
#include "../../util/util_error.h"
#include "../../util/util_string.h"
namespace dxvk::vk {
/**
* \brief Name list
*
* Stores a list of extension or layer names.
* Note that only name constants may be added
* to a name list. Adding dynamically allocated
* strings will result in udefined behaviour.
*/
class NameList {
public:
void add(const char* name) {
m_list.push_back(name);
}
auto name(size_t i) const {
return m_list[i];
}
auto names() const { return m_list.data(); }
auto count() const { return m_list.size(); }
private:
std::vector<const char*> m_list;
};
/**
* \brief Name set
*
* Stores a set of supported layers or extensions and
* provides methods to query their support status.
*/
class NameSet {
public:
/**
* \brief Checks whether an extension or layer is supported
*
* \param [in] name The layer or extension name
* \returns \c true if the entity is supported
*/
bool supports(const char* name) const;
/**
* \brief Enumerates instance layers
*
* \param [in] vkl Vulkan library functions
* \returns Available instance layers
*/
static NameSet enumerateInstanceLayers(
const LibraryFn& vkl);
/**
* \brief Enumerates instance extensions
*
* \param [in] vkl Vulkan library functions
* \param [in] layers Enabled instance layers
* \returns Available instance extensions
*/
static NameSet enumerateInstanceExtensions(
const LibraryFn& vkl,
const NameList& layers);
/**
* \brief Enumerates device extensions
*
* \param [in] vki Vulkan instance functions
* \param [in] device The physical device
* \returns Available device extensions
*/
static NameSet enumerateDeviceExtensions(
const InstanceFn& vki,
VkPhysicalDevice device);
private:
std::unordered_set<std::string> m_names;
void addInstanceLayerExtensions(
const LibraryFn& vkl,
const char* layer);
};
}

View File

@ -0,0 +1,43 @@
#include "dxvk_vulkan_loader.h"
namespace dxvk::vk {
PFN_vkVoidFunction LibraryLoader::sym(const char* name) const {
return ::vkGetInstanceProcAddr(nullptr, name);
}
InstanceLoader::InstanceLoader(VkInstance instance)
: m_instance(instance) { }
PFN_vkVoidFunction InstanceLoader::sym(const char* name) const {
return ::vkGetInstanceProcAddr(m_instance, name);
}
DeviceLoader::DeviceLoader(VkInstance instance, VkDevice device)
: m_getDeviceProcAddr(reinterpret_cast<PFN_vkGetDeviceProcAddr>(
::vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr"))),
m_device(device) { }
PFN_vkVoidFunction DeviceLoader::sym(const char* name) const {
return m_getDeviceProcAddr(m_device, name);
}
LibraryFn::LibraryFn() { }
LibraryFn::~LibraryFn() { }
InstanceFn::InstanceFn(VkInstance instance)
: InstanceLoader(instance) { }
InstanceFn::~InstanceFn() { }
DeviceFn::DeviceFn(VkInstance instance, VkDevice device)
: DeviceLoader(instance, device) { }
DeviceFn::~DeviceFn() { }
}

View File

@ -0,0 +1,267 @@
#pragma once
#include "../../util/rc/util_rc.h"
#include "../../util/rc/util_rc_ptr.h"
#include "dxvk_vulkan_loader_fn.h"
namespace dxvk::vk {
/**
* \brief Vulkan library loader
*
* Provides methods to load Vulkan functions that
* can be called before creating a instance.
*/
struct LibraryLoader : public RcObject {
PFN_vkVoidFunction sym(const char* name) const;
};
/**
* \brief Vulkan instance loader
*
* Loads Vulkan functions that can be
* called for a specific instance.
*/
struct InstanceLoader : public RcObject {
InstanceLoader(VkInstance instance);
PFN_vkVoidFunction sym(const char* name) const;
VkInstance instance() const { return m_instance; }
private:
const VkInstance m_instance;
};
/**
* \brief Vulkan device loader
*
* Loads Vulkan functions for a
* specific device.
*/
struct DeviceLoader : public RcObject {
DeviceLoader(VkInstance instance, VkDevice device);
PFN_vkVoidFunction sym(const char* name) const;
VkDevice device() const { return m_device; }
private:
const PFN_vkGetDeviceProcAddr m_getDeviceProcAddr;
const VkDevice m_device;
};
/**
* \brief Vulkan library functions
*
* Vulkan functions that are called before
* creating an actual Vulkan instance.
*/
struct LibraryFn : LibraryLoader {
LibraryFn();
~LibraryFn();
VULKAN_FN(vkCreateInstance);
VULKAN_FN(vkEnumerateInstanceLayerProperties);
VULKAN_FN(vkEnumerateInstanceExtensionProperties);
};
/**
* \brief Vulkan instance functions
*
* Vulkan functions for a given instance that
* are independent of any Vulkan devices.
*/
struct InstanceFn : InstanceLoader {
InstanceFn(VkInstance instance);
~InstanceFn();
VULKAN_FN(vkCreateDevice);
VULKAN_FN(vkDestroyInstance);
VULKAN_FN(vkEnumerateDeviceExtensionProperties);
VULKAN_FN(vkEnumeratePhysicalDevices);
VULKAN_FN(vkGetPhysicalDeviceFeatures);
VULKAN_FN(vkGetPhysicalDeviceFormatProperties);
VULKAN_FN(vkGetPhysicalDeviceImageFormatProperties);
VULKAN_FN(vkGetPhysicalDeviceMemoryProperties);
VULKAN_FN(vkGetPhysicalDeviceProperties);
VULKAN_FN(vkGetPhysicalDeviceQueueFamilyProperties);
VULKAN_FN(vkGetPhysicalDeviceSparseImageFormatProperties);
#ifdef VK_KHR_surface
#ifdef VK_USE_PLATFORM_XCB_KHR
VULKAN_FN(vkCreateXcbSurfaceKHR);
VULKAN_FN(vkGetPhysicalDeviceXcbPresentationSupportKHR);
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
VULKAN_FN(vkCreateXlibSurfaceKHR);
VULKAN_FN(vkGetPhysicalDeviceXlibPresentationSupportKHR);
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
VULKAN_FN(vkCreateWaylandSurfaceKHR);
VULKAN_FN(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
VULKAN_FN(vkCreateWin32SurfaceKHR);
VULKAN_FN(vkGetPhysicalDeviceWin32PresentationSupportKHR);
#endif
VULKAN_FN(vkDestroySurfaceKHR);
VULKAN_FN(vkGetPhysicalDeviceSurfaceSupportKHR);
VULKAN_FN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
VULKAN_FN(vkGetPhysicalDeviceSurfaceFormatsKHR);
VULKAN_FN(vkGetPhysicalDeviceSurfacePresentModesKHR);
#endif
#ifdef VK_EXT_debug_report
VULKAN_FN(vkCreateDebugReportCallbackEXT);
VULKAN_FN(vkDestroyDebugReportCallbackEXT);
VULKAN_FN(vkDebugReportMessageEXT);
#endif
};
/**
* \brief Vulkan device functions
*
* Vulkan functions for a specific Vulkan device.
* This ensures that no slow dispatch code is executed.
*/
struct DeviceFn : DeviceLoader {
DeviceFn(VkInstance instance, VkDevice device);
~DeviceFn();
VULKAN_FN(vkDestroyDevice);
VULKAN_FN(vkGetDeviceQueue);
VULKAN_FN(vkQueueSubmit);
VULKAN_FN(vkQueueWaitIdle);
VULKAN_FN(vkDeviceWaitIdle);
VULKAN_FN(vkAllocateMemory);
VULKAN_FN(vkFreeMemory);
VULKAN_FN(vkMapMemory);
VULKAN_FN(vkUnmapMemory);
VULKAN_FN(vkFlushMappedMemoryRanges);
VULKAN_FN(vkInvalidateMappedMemoryRanges);
VULKAN_FN(vkGetDeviceMemoryCommitment);
VULKAN_FN(vkBindBufferMemory);
VULKAN_FN(vkBindImageMemory);
VULKAN_FN(vkGetBufferMemoryRequirements);
VULKAN_FN(vkGetImageMemoryRequirements);
VULKAN_FN(vkGetImageSparseMemoryRequirements);
VULKAN_FN(vkQueueBindSparse);
VULKAN_FN(vkCreateFence);
VULKAN_FN(vkDestroyFence);
VULKAN_FN(vkResetFences);
VULKAN_FN(vkGetFenceStatus);
VULKAN_FN(vkWaitForFences);
VULKAN_FN(vkCreateSemaphore);
VULKAN_FN(vkDestroySemaphore);
VULKAN_FN(vkCreateEvent);
VULKAN_FN(vkDestroyEvent);
VULKAN_FN(vkGetEventStatus);
VULKAN_FN(vkSetEvent);
VULKAN_FN(vkResetEvent);
VULKAN_FN(vkCreateQueryPool);
VULKAN_FN(vkDestroyQueryPool);
VULKAN_FN(vkGetQueryPoolResults);
VULKAN_FN(vkCreateBuffer);
VULKAN_FN(vkDestroyBuffer);
VULKAN_FN(vkCreateBufferView);
VULKAN_FN(vkDestroyBufferView);
VULKAN_FN(vkCreateImage);
VULKAN_FN(vkDestroyImage);
VULKAN_FN(vkGetImageSubresourceLayout);
VULKAN_FN(vkCreateImageView);
VULKAN_FN(vkDestroyImageView);
VULKAN_FN(vkCreateShaderModule);
VULKAN_FN(vkDestroyShaderModule);
VULKAN_FN(vkCreatePipelineCache);
VULKAN_FN(vkDestroyPipelineCache);
VULKAN_FN(vkGetPipelineCacheData);
VULKAN_FN(vkMergePipelineCaches);
VULKAN_FN(vkCreateGraphicsPipelines);
VULKAN_FN(vkCreateComputePipelines);
VULKAN_FN(vkDestroyPipeline);
VULKAN_FN(vkCreatePipelineLayout);
VULKAN_FN(vkDestroyPipelineLayout);
VULKAN_FN(vkCreateSampler);
VULKAN_FN(vkDestroySampler);
VULKAN_FN(vkCreateDescriptorSetLayout);
VULKAN_FN(vkDestroyDescriptorSetLayout);
VULKAN_FN(vkCreateDescriptorPool);
VULKAN_FN(vkDestroyDescriptorPool);
VULKAN_FN(vkResetDescriptorPool);
VULKAN_FN(vkAllocateDescriptorSets);
VULKAN_FN(vkFreeDescriptorSets);
VULKAN_FN(vkUpdateDescriptorSets);
VULKAN_FN(vkCreateFramebuffer);
VULKAN_FN(vkDestroyFramebuffer);
VULKAN_FN(vkCreateRenderPass);
VULKAN_FN(vkDestroyRenderPass);
VULKAN_FN(vkGetRenderAreaGranularity);
VULKAN_FN(vkCreateCommandPool);
VULKAN_FN(vkDestroyCommandPool);
VULKAN_FN(vkResetCommandPool);
VULKAN_FN(vkAllocateCommandBuffers);
VULKAN_FN(vkFreeCommandBuffers);
VULKAN_FN(vkBeginCommandBuffer);
VULKAN_FN(vkEndCommandBuffer);
VULKAN_FN(vkResetCommandBuffer);
VULKAN_FN(vkCmdBindPipeline);
VULKAN_FN(vkCmdSetViewport);
VULKAN_FN(vkCmdSetScissor);
VULKAN_FN(vkCmdSetLineWidth);
VULKAN_FN(vkCmdSetDepthBias);
VULKAN_FN(vkCmdSetBlendConstants);
VULKAN_FN(vkCmdSetDepthBounds);
VULKAN_FN(vkCmdSetStencilCompareMask);
VULKAN_FN(vkCmdSetStencilWriteMask);
VULKAN_FN(vkCmdSetStencilReference);
VULKAN_FN(vkCmdBindDescriptorSets);
VULKAN_FN(vkCmdBindIndexBuffer);
VULKAN_FN(vkCmdBindVertexBuffers);
VULKAN_FN(vkCmdDraw);
VULKAN_FN(vkCmdDrawIndexed);
VULKAN_FN(vkCmdDrawIndirect);
VULKAN_FN(vkCmdDrawIndexedIndirect);
VULKAN_FN(vkCmdDispatch);
VULKAN_FN(vkCmdDispatchIndirect);
VULKAN_FN(vkCmdCopyBuffer);
VULKAN_FN(vkCmdCopyImage);
VULKAN_FN(vkCmdBlitImage);
VULKAN_FN(vkCmdCopyBufferToImage);
VULKAN_FN(vkCmdCopyImageToBuffer);
VULKAN_FN(vkCmdUpdateBuffer);
VULKAN_FN(vkCmdFillBuffer);
VULKAN_FN(vkCmdClearColorImage);
VULKAN_FN(vkCmdClearDepthStencilImage);
VULKAN_FN(vkCmdClearAttachments);
VULKAN_FN(vkCmdResolveImage);
VULKAN_FN(vkCmdSetEvent);
VULKAN_FN(vkCmdResetEvent);
VULKAN_FN(vkCmdWaitEvents);
VULKAN_FN(vkCmdPipelineBarrier);
VULKAN_FN(vkCmdBeginQuery);
VULKAN_FN(vkCmdEndQuery);
VULKAN_FN(vkCmdResetQueryPool);
VULKAN_FN(vkCmdWriteTimestamp);
VULKAN_FN(vkCmdCopyQueryPoolResults);
VULKAN_FN(vkCmdPushConstants);
VULKAN_FN(vkCmdBeginRenderPass);
VULKAN_FN(vkCmdNextSubpass);
VULKAN_FN(vkCmdEndRenderPass);
VULKAN_FN(vkCmdExecuteCommands);
#ifdef VK_KHR_swapchain
VULKAN_FN(vkCreateSwapchainKHR);
VULKAN_FN(vkDestroySwapchainKHR);
VULKAN_FN(vkGetSwapchainImagesKHR);
VULKAN_FN(vkAcquireNextImageKHR);
VULKAN_FN(vkQueuePresentKHR);
#endif
};
}

View File

@ -0,0 +1,48 @@
#pragma once
#define VK_USE_PLATFORM_WIN32_KHR 1
#include <vulkan/vulkan.h>
#define VULKAN_FN(name) \
VulkanFn<::PFN_ ## name> name = sym(#name)
namespace dxvk::vk {
template<typename Fn>
class VulkanFn;
/**
* \brief Vulkan function
*
* Wraps an Vulkan function pointer and provides
* a call operator using the correct types.
*/
template<typename Ret, typename... Args>
class VulkanFn<Ret (VKAPI_PTR*)(Args...)> {
using Fn = Ret (VKAPI_PTR*)(Args...);
public:
VulkanFn() { }
VulkanFn(Fn ptr)
: m_fn(ptr) { }
VulkanFn(PFN_vkVoidFunction ptr)
: m_fn(reinterpret_cast<Fn>(ptr)) { }
/**
* \brief Invokes Vulkan function
*
* \param [in] args Arguments
* \returns Function return value
*/
Ret operator () (Args... args) const {
return (*m_fn)(args...);
}
private:
Fn m_fn = nullptr;
};
}

2
src/meson.build Normal file
View File

@ -0,0 +1,2 @@
subdir('util')
subdir('dxvk')

26
src/util/log/log.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "log.h"
namespace dxvk {
Log::Log(const std::string& filename)
: m_stream(filename, std::ios_base::out | std::ios_base::trunc) {
}
Log::~Log() {
}
void Log::log(const std::string& message) {
std::lock_guard<std::mutex> lock(m_mutex);
std::cerr << message << std::endl;
std::cerr.flush();
m_stream << message << std::endl;
m_stream.flush();
}
}

40
src/util/log/log.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <fstream>
#include <iostream>
#include <mutex>
#include <string>
#include "../rc/util_rc.h"
namespace dxvk {
/**
* \brief Logger
*
* Logs messages generated by DXVK and
* the client APIs using DXVK.
*/
class Log : public RcObject {
public:
Log(const std::string& filename);
~Log();
/**
* \brief Adds a message to the log
*
* Prints the message to stderr and appends
* it to the log file at the same time.
*/
void log(const std::string& message);
private:
std::mutex m_mutex;
std::fstream m_stream;
};
}

6
src/util/meson.build Normal file
View File

@ -0,0 +1,6 @@
util_src = files([
'log/log.cpp',
])
util_lib = static_library('util', util_src,
include_directories : [ dxvk_include_path ])

36
src/util/rc/util_rc.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <atomic>
namespace dxvk {
/**
* \brief Reference-counted object
*/
class RcObject {
public:
/**
* \brief Increments reference count
* \returns New reference count
*/
uint32_t incRef() {
return ++m_refCount;
}
/**
* \brief Decrements reference count
* \returns New reference count
*/
uint32_t decRef() {
return --m_refCount;
}
private:
std::atomic<uint32_t> m_refCount = { 0u };
};
}

126
src/util/rc/util_rc_ptr.h Normal file
View File

@ -0,0 +1,126 @@
#pragma once
#include <functional>
namespace dxvk {
/**
* \brief Pointer for reference-counted objects
*
* This only requires the given type to implement \c incRef
* and \c decRef methods that adjust the reference count.
* \tparam T Object type
*/
template<typename T>
class Rc {
template<typename Tx>
friend class Rc;
public:
Rc() { }
Rc(std::nullptr_t) { }
Rc(T* object)
: m_object(object) {
this->incRef();
}
Rc(const Rc& other)
: m_object(other.m_object) {
this->incRef();
}
template<typename Tx>
Rc(const Rc<Tx>& other)
: m_object(other.m_object) {
this->incRef();
}
Rc(Rc&& other)
: m_object(other.m_object) {
other.m_object = nullptr;
}
template<typename Tx>
Rc(Rc<Tx>&& other)
: m_object(other.m_object) {
other.m_object = nullptr;
}
Rc& operator = (std::nullptr_t) {
this->decRef();
m_object = nullptr;
return *this;
}
Rc& operator = (const Rc& other) {
other.incRef();
this->decRef();
m_object = other.m_object;
return *this;
}
template<typename Tx>
Rc& operator = (const Rc<Tx>& other) {
other.incRef();
this->decRef();
m_object = other.m_object;
return *this;
}
Rc& operator = (Rc&& other) {
this->decRef();
this->m_object = other.m_object;
other.m_object = nullptr;
return *this;
}
template<typename Tx>
Rc& operator = (Rc<Tx>&& other) {
this->decRef();
this->m_object = other.m_object;
other.m_object = nullptr;
return *this;
}
~Rc() {
this->decRef();
}
T& operator * () const { return *m_object; }
T* operator -> () const { return m_object; }
T* ptr() const { return m_object; }
bool operator == (const Rc& other) const { return m_object == other.m_object; }
bool operator != (const Rc& other) const { return m_object != other.m_object; }
bool operator == (std::nullptr_t) const { return m_object == nullptr; }
bool operator != (std::nullptr_t) const { return m_object != nullptr; }
private:
T* m_object = nullptr;
void incRef() const {
if (m_object != nullptr)
m_object->incRef();
}
void decRef() const {
if (m_object != nullptr) {
if (m_object->decRef() == 0)
delete m_object;
}
}
};
template<typename T>
struct RcHash {
size_t operator () (const Rc<T>& rc) const {
return std::hash<T*>()(rc.ptr());
}
};
}

31
src/util/util_error.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <string>
namespace dxvk {
/**
* \brief DXVK error
*
* A generic exception class that stores a
* message. Exceptions should be logged.
*/
class DxvkError {
public:
DxvkError() { }
DxvkError(std::string&& message)
: m_message(std::move(message)) { }
const std::string& message() const {
return m_message;
}
private:
std::string m_message;
};
}

12
src/util/util_math.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
namespace dxvk {
template<typename T>
T clamp(T n, T lo, T hi) {
if (n < lo) return lo;
if (n > hi) return hi;
return n;
}
}

23
src/util/util_string.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <sstream>
namespace dxvk::str {
inline void format1(std::stringstream&) { }
template<typename T, typename... Tx>
void format1(std::stringstream& str, const T& arg, const Tx&... args) {
str << arg;
format1(str, args...);
}
template<typename... Args>
std::string format(const Args&... args) {
std::stringstream stream;
format1(stream, args...);
return stream.str();
}
}

3
tests/dxvk/meson.build Normal file
View File

@ -0,0 +1,3 @@
test_dxvk_deps = [ dxvk_dep ]
executable('dxvk-triangle', files('test_dxvk_triangle.cpp'), dependencies: test_dxvk_deps)

View File

@ -0,0 +1,128 @@
#include <dxvk_framebuffer.h>
#include <dxvk_instance.h>
#include <dxvk_main.h>
#include <dxvk_surface.h>
#include <windows.h>
#include <windowsx.h>
using namespace dxvk;
class TriangleApp {
public:
TriangleApp(HINSTANCE instance, HWND window)
: m_dxvkInstance (new DxvkInstance()),
m_dxvkAdapter (m_dxvkInstance->enumAdapters().at(0)),
m_dxvkDevice (m_dxvkAdapter->createDevice()),
m_dxvkSurface (m_dxvkAdapter->createSurface(instance, window)),
m_dxvkSwapchain (m_dxvkDevice->createSwapchain(m_dxvkSurface,
DxvkSwapchainProperties {
VkSurfaceFormatKHR { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR },
VK_PRESENT_MODE_FIFO_KHR,
VkExtent2D { 640, 480 },
})),
m_dxvkContext (m_dxvkDevice->createContext()),
m_dxvkCommandList (m_dxvkDevice->createCommandList()) {
}
~TriangleApp() {
}
void run() {
auto sync1 = m_dxvkDevice->createSemaphore();
auto sync2 = m_dxvkDevice->createSemaphore();
m_dxvkContext->beginRecording(m_dxvkCommandList);
m_dxvkContext->setFramebuffer(
m_dxvkSwapchain->getFramebuffer(sync1));
m_dxvkContext->endRecording();
auto fence = m_dxvkDevice->submitCommandList(
m_dxvkCommandList, sync1, sync2);
m_dxvkSwapchain->present(sync2);
m_dxvkDevice->waitForIdle();
m_dxvkCommandList->reset();
}
private:
Rc<DxvkInstance> m_dxvkInstance;
Rc<DxvkAdapter> m_dxvkAdapter;
Rc<DxvkDevice> m_dxvkDevice;
Rc<DxvkSurface> m_dxvkSurface;
Rc<DxvkSwapchain> m_dxvkSwapchain;
Rc<DxvkContext> m_dxvkContext;
Rc<DxvkCommandList> m_dxvkCommandList;
Rc<DxvkBuffer> m_vertexBuffer;
};
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
HWND hWnd;
WNDCLASSEXW wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
RegisterClassExW(&wc);
hWnd = CreateWindowExW(0,
L"WindowClass1",
L"Our First Windowed Program",
WS_OVERLAPPEDWINDOW,
300, 300,
640, 480,
nullptr,
nullptr,
hInstance,
nullptr);
ShowWindow(hWnd, nCmdShow);
MSG msg;
TriangleApp app(hInstance, hWnd);
try {
while (true) {
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
return msg.wParam;
} else {
app.run();
}
}
} catch (const dxvk::DxvkError& e) {
dxvk::log(e.message());
return msg.wParam;
}
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

1
tests/meson.build Normal file
View File

@ -0,0 +1 @@
subdir('dxvk')