mirror of
https://github.com/redstrate/dxbc.git
synced 2025-04-20 11:47:46 +00:00
Add initial files
This commit is contained in:
commit
584a7c6533
84 changed files with 24108 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.directory
|
||||||
|
*build*/
|
||||||
|
.idea/
|
48
CMakeLists.txt
Normal file
48
CMakeLists.txt
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
project(dxbc LANGUAGES CXX)
|
||||||
|
|
||||||
|
add_library(windows-headers INTERFACE)
|
||||||
|
target_include_directories(windows-headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/windows)
|
||||||
|
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
|
||||||
|
add_subdirectory(src/util)
|
||||||
|
add_subdirectory(src/spirv)
|
||||||
|
|
||||||
|
add_library(dxbc STATIC)
|
||||||
|
target_sources(dxbc PRIVATE
|
||||||
|
src/dxbc/dxbc_analysis.cpp
|
||||||
|
src/dxbc/dxbc_analysis.h
|
||||||
|
src/dxbc/dxbc_chunk_isgn.cpp
|
||||||
|
src/dxbc/dxbc_chunk_isgn.h
|
||||||
|
src/dxbc/dxbc_chunk_shex.cpp
|
||||||
|
src/dxbc/dxbc_chunk_shex.h
|
||||||
|
src/dxbc/dxbc_common.cpp
|
||||||
|
src/dxbc/dxbc_common.h
|
||||||
|
src/dxbc/dxbc_compiler.cpp
|
||||||
|
src/dxbc/dxbc_compiler.h
|
||||||
|
src/dxbc/dxbc_decoder.cpp
|
||||||
|
src/dxbc/dxbc_decoder.h
|
||||||
|
src/dxbc/dxbc_defs.cpp
|
||||||
|
src/dxbc/dxbc_defs.h
|
||||||
|
src/dxbc/dxbc_enums.h
|
||||||
|
src/dxbc/dxbc_header.cpp
|
||||||
|
src/dxbc/dxbc_header.h
|
||||||
|
src/dxbc/dxbc_include.h
|
||||||
|
src/dxbc/dxbc_modinfo.h
|
||||||
|
src/dxbc/dxbc_module.cpp
|
||||||
|
src/dxbc/dxbc_module.h
|
||||||
|
src/dxbc/dxbc_names.cpp
|
||||||
|
src/dxbc/dxbc_names.h
|
||||||
|
src/dxbc/dxbc_options.cpp
|
||||||
|
src/dxbc/dxbc_options.h
|
||||||
|
src/dxbc/dxbc_reader.cpp
|
||||||
|
src/dxbc/dxbc_reader.h
|
||||||
|
src/dxbc/dxbc_tag.h
|
||||||
|
src/dxbc/dxbc_util.cpp
|
||||||
|
src/dxbc/dxbc_util.h
|
||||||
|
)
|
||||||
|
target_include_directories(dxbc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(dxbc PUBLIC dxbc-util dxbc-spirv Vulkan::Vulkan)
|
||||||
|
|
||||||
|
add_subdirectory(example)
|
22
LICENSE
Normal file
22
LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2017 Philip Rebohle
|
||||||
|
Copyright (c) 2019 Joshua Ashton
|
||||||
|
|
||||||
|
zlib/libpng license
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
– The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
– Altered source versions must be plainly marked as such, and must not
|
||||||
|
be misrepresented as being the original software.
|
||||||
|
|
||||||
|
– This notice may not be removed or altered from any source distribution.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# dxbc
|
||||||
|
|
||||||
|
This is the `dxbc` decompiler pulled directly from the [DXVK source tree](https://github.com/doitsujin/dxvk). This is for my personal use, do not expect me to keep updating this.
|
||||||
|
|
||||||
|
See an example of how to use the library in `example/`.
|
||||||
|
|
||||||
|
All credit goes to the DXVK developers, of course.
|
3
example/CMakeLists.txt
Normal file
3
example/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
add_executable(dxbc-example)
|
||||||
|
target_sources(dxbc-example PRIVATE main.cpp)
|
||||||
|
target_link_libraries(dxbc-example PRIVATE dxbc spirv-cross-core spirv-cross-glsl)
|
35
example/main.cpp
Normal file
35
example/main.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <dxbc/dxbc_module.h>
|
||||||
|
#include <util/log/log.h>
|
||||||
|
|
||||||
|
#include <spirv_cross/spirv_glsl.hpp>
|
||||||
|
|
||||||
|
dxvk::Logger dxvk::Logger::s_instance("dxbc.log");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// pass in the DXVK binary you want to decompile
|
||||||
|
const std::string inputFile = argv[1];
|
||||||
|
std::ifstream infile(inputFile, std::ios_base::binary);
|
||||||
|
|
||||||
|
std::vector<char> buffer((std::istreambuf_iterator<char>(infile)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
dxvk::DxbcReader reader(buffer.data(), buffer.size());
|
||||||
|
|
||||||
|
dxvk::DxbcModule module(reader);
|
||||||
|
|
||||||
|
dxvk::DxbcModuleInfo info;
|
||||||
|
auto result = module.compile(info, "test");
|
||||||
|
|
||||||
|
spirv_cross::CompilerGLSL glsl(result.code.data(), result.code.dwords());
|
||||||
|
|
||||||
|
spirv_cross::CompilerGLSL::Options options;
|
||||||
|
options.version = 310;
|
||||||
|
options.es = true;
|
||||||
|
glsl.set_common_options(options);
|
||||||
|
|
||||||
|
std::cout << glsl.compile() << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
3
include/windows/oaidl.h
Normal file
3
include/windows/oaidl.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
3
include/windows/objbase.h
Normal file
3
include/windows/objbase.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
3
include/windows/ocidl.h
Normal file
3
include/windows/ocidl.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
3
include/windows/ole2.h
Normal file
3
include/windows/ole2.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
8
include/windows/poppack.h
Normal file
8
include/windows/poppack.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* This file has no copyright assigned and is placed in the Public Domain.
|
||||||
|
* This file is part of the mingw-w64 runtime package.
|
||||||
|
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
|
||||||
|
*/
|
||||||
|
#if !(defined(lint) || defined(RC_INVOKED))
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
8
include/windows/pshpack4.h
Normal file
8
include/windows/pshpack4.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* This file has no copyright assigned and is placed in the Public Domain.
|
||||||
|
* This file is part of the mingw-w64 runtime package.
|
||||||
|
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
|
||||||
|
*/
|
||||||
|
#if !(defined(lint) || defined(RC_INVOKED))
|
||||||
|
#pragma pack(push,4)
|
||||||
|
#endif
|
3
include/windows/rpc.h
Normal file
3
include/windows/rpc.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
3
include/windows/rpcndr.h
Normal file
3
include/windows/rpcndr.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Don't care.
|
52
include/windows/unknwn.h
Normal file
52
include/windows/unknwn.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "windows_base.h"
|
||||||
|
|
||||||
|
typedef interface IUnknown IUnknown;
|
||||||
|
|
||||||
|
DEFINE_GUID(IID_IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
struct IUnknown {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) = 0;
|
||||||
|
template<class Q>
|
||||||
|
HRESULT STDMETHODCALLTYPE QueryInterface(Q **pp) {
|
||||||
|
return QueryInterface(__uuidof(Q), (void **)pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ULONG AddRef() = 0;
|
||||||
|
virtual ULONG Release() = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
typedef struct IUnknownVtbl
|
||||||
|
{
|
||||||
|
BEGIN_INTERFACE
|
||||||
|
|
||||||
|
HRESULT (STDMETHODCALLTYPE *QueryInterface)(
|
||||||
|
IUnknown *This,
|
||||||
|
REFIID riid,
|
||||||
|
void **ppvObject
|
||||||
|
);
|
||||||
|
ULONG (STDMETHODCALLTYPE *AddRef)(IUnknown *This);
|
||||||
|
ULONG (STDMETHODCALLTYPE *Release)(IUnknown *This);
|
||||||
|
|
||||||
|
END_INTERFACE
|
||||||
|
} IUnknownVtbl;
|
||||||
|
|
||||||
|
interface IUnknown
|
||||||
|
{
|
||||||
|
CONST_VTBL struct IUnknownVtbl *lpVtbl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IUnknown_AddRef(This) ((This)->lpVtbl->AddRef(This))
|
||||||
|
#define IUnknown_Release(This) ((This)->lpVtbl->Release(This))
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
DECLARE_UUIDOF_HELPER(IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46)
|
||||||
|
|
||||||
|
#define IID_PPV_ARGS(ppType) __uuidof(decltype(**(ppType))), [](auto** pp) { (void)static_cast<IUnknown*>(*pp); return reinterpret_cast<void**>(pp); }(ppType)
|
4
include/windows/windows.h
Normal file
4
include/windows/windows.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "windows_base.h"
|
||||||
|
#include "unknwn.h"
|
389
include/windows/windows_base.h
Normal file
389
include/windows/windows_base.h
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
// GCC complains about the COM interfaces
|
||||||
|
// not having virtual destructors
|
||||||
|
|
||||||
|
// and class conversion for C...DESC helper types
|
||||||
|
#if defined(__GNUC__) && defined(__cplusplus)
|
||||||
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#pragma GCC diagnostic ignored "-Wclass-conversion"
|
||||||
|
#endif // __GNUC__ && __cplusplus
|
||||||
|
|
||||||
|
typedef int32_t INT;
|
||||||
|
typedef uint32_t UINT;
|
||||||
|
|
||||||
|
typedef int32_t LONG;
|
||||||
|
typedef uint32_t ULONG;
|
||||||
|
typedef int32_t *LPLONG;
|
||||||
|
|
||||||
|
typedef int32_t HRESULT;
|
||||||
|
|
||||||
|
typedef wchar_t WCHAR;
|
||||||
|
typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;
|
||||||
|
typedef unsigned char UCHAR, *PUCHAR;
|
||||||
|
|
||||||
|
typedef char CHAR;
|
||||||
|
typedef const CHAR *LPCSTR, *PCSTR;
|
||||||
|
|
||||||
|
typedef INT BOOL;
|
||||||
|
typedef BOOL WINBOOL;
|
||||||
|
|
||||||
|
typedef uint16_t UINT16;
|
||||||
|
typedef uint32_t UINT32;
|
||||||
|
typedef uint64_t UINT64;
|
||||||
|
typedef void VOID;
|
||||||
|
typedef void* PVOID;
|
||||||
|
typedef void* LPVOID;
|
||||||
|
typedef const void* LPCVOID;
|
||||||
|
|
||||||
|
typedef size_t SIZE_T;
|
||||||
|
|
||||||
|
typedef int8_t INT8;
|
||||||
|
typedef uint8_t UINT8;
|
||||||
|
typedef uint8_t BYTE;
|
||||||
|
|
||||||
|
typedef int16_t SHORT;
|
||||||
|
typedef uint16_t USHORT;
|
||||||
|
|
||||||
|
typedef int64_t LONGLONG;
|
||||||
|
typedef int64_t INT64;
|
||||||
|
|
||||||
|
typedef uint64_t ULONGLONG;
|
||||||
|
typedef uint64_t UINT64;
|
||||||
|
|
||||||
|
typedef intptr_t LONG_PTR;
|
||||||
|
typedef uintptr_t ULONG_PTR;
|
||||||
|
|
||||||
|
typedef float FLOAT;
|
||||||
|
|
||||||
|
#ifndef GUID_DEFINED
|
||||||
|
#define GUID_DEFINED
|
||||||
|
typedef struct GUID {
|
||||||
|
uint32_t Data1;
|
||||||
|
uint16_t Data2;
|
||||||
|
uint16_t Data3;
|
||||||
|
uint8_t Data4[8];
|
||||||
|
} GUID;
|
||||||
|
#endif // GUID_DEFINED
|
||||||
|
|
||||||
|
typedef GUID UUID;
|
||||||
|
typedef GUID IID;
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define REFIID const IID&
|
||||||
|
#define REFGUID const GUID&
|
||||||
|
#define REFCLSID const GUID&
|
||||||
|
#else
|
||||||
|
#define REFIID const IID*
|
||||||
|
#define REFGUID const GUID*
|
||||||
|
#define REFCLSID const GUID* const
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr GUID __uuidof_helper();
|
||||||
|
|
||||||
|
#define __uuidof(T) __uuidof_helper<T>()
|
||||||
|
#define __uuidof_var(T) __uuidof_helper<decltype(T)>()
|
||||||
|
|
||||||
|
inline bool operator==(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) == 0; }
|
||||||
|
inline bool operator!=(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) != 0; }
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
typedef uint32_t DWORD;
|
||||||
|
typedef uint16_t WORD;
|
||||||
|
typedef DWORD *LPDWORD;
|
||||||
|
|
||||||
|
typedef void* HANDLE;
|
||||||
|
typedef HANDLE HMONITOR;
|
||||||
|
typedef HANDLE HDC;
|
||||||
|
typedef HANDLE HMODULE;
|
||||||
|
typedef HANDLE HINSTANCE;
|
||||||
|
typedef HANDLE HWND;
|
||||||
|
typedef HANDLE HKEY;
|
||||||
|
typedef HANDLE *LPHANDLE;
|
||||||
|
typedef DWORD COLORREF;
|
||||||
|
|
||||||
|
#if INTPTR_MAX == INT64_MAX
|
||||||
|
typedef int64_t INT_PTR;
|
||||||
|
typedef uint64_t UINT_PTR;
|
||||||
|
#else
|
||||||
|
typedef int32_t INT_PTR;
|
||||||
|
typedef uint32_t UINT_PTR;
|
||||||
|
#endif
|
||||||
|
typedef INT_PTR* PINT_PTR;
|
||||||
|
typedef UINT_PTR* PUINT_PTR;
|
||||||
|
|
||||||
|
#ifdef STRICT
|
||||||
|
#define DECLARE_HANDLE(a) typedef struct a##__ { int unused; } *a
|
||||||
|
#else /*STRICT*/
|
||||||
|
#define DECLARE_HANDLE(a) typedef HANDLE a
|
||||||
|
#endif /*STRICT*/
|
||||||
|
|
||||||
|
typedef char* LPSTR;
|
||||||
|
typedef wchar_t* LPWSTR;
|
||||||
|
typedef const char* LPCSTR;
|
||||||
|
typedef const wchar_t* LPCWSTR;
|
||||||
|
|
||||||
|
typedef struct LUID {
|
||||||
|
DWORD LowPart;
|
||||||
|
LONG HighPart;
|
||||||
|
} LUID;
|
||||||
|
|
||||||
|
typedef struct POINT {
|
||||||
|
LONG x;
|
||||||
|
LONG y;
|
||||||
|
} POINT;
|
||||||
|
|
||||||
|
typedef POINT* LPPOINT;
|
||||||
|
|
||||||
|
typedef struct RECT {
|
||||||
|
LONG left;
|
||||||
|
LONG top;
|
||||||
|
LONG right;
|
||||||
|
LONG bottom;
|
||||||
|
} RECT,*PRECT,*NPRECT,*LPRECT;
|
||||||
|
|
||||||
|
typedef struct SIZE {
|
||||||
|
LONG cx;
|
||||||
|
LONG cy;
|
||||||
|
} SIZE,*PSIZE,*LPSIZE;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
DWORD LowPart;
|
||||||
|
LONG HighPart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
DWORD LowPart;
|
||||||
|
LONG HighPart;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
LONGLONG QuadPart;
|
||||||
|
} LARGE_INTEGER;
|
||||||
|
|
||||||
|
typedef struct MEMORYSTATUS {
|
||||||
|
DWORD dwLength;
|
||||||
|
SIZE_T dwTotalPhys;
|
||||||
|
} MEMORYSTATUS;
|
||||||
|
|
||||||
|
typedef struct SECURITY_ATTRIBUTES {
|
||||||
|
DWORD nLength;
|
||||||
|
void* lpSecurityDescriptor;
|
||||||
|
BOOL bInheritHandle;
|
||||||
|
} SECURITY_ATTRIBUTES;
|
||||||
|
|
||||||
|
typedef struct PALETTEENTRY {
|
||||||
|
BYTE peRed;
|
||||||
|
BYTE peGreen;
|
||||||
|
BYTE peBlue;
|
||||||
|
BYTE peFlags;
|
||||||
|
} PALETTEENTRY, *PPALETTEENTRY, *LPPALETTEENTRY;
|
||||||
|
|
||||||
|
typedef struct RGNDATAHEADER {
|
||||||
|
DWORD dwSize;
|
||||||
|
DWORD iType;
|
||||||
|
DWORD nCount;
|
||||||
|
DWORD nRgnSize;
|
||||||
|
RECT rcBound;
|
||||||
|
} RGNDATAHEADER;
|
||||||
|
|
||||||
|
typedef struct RGNDATA {
|
||||||
|
RGNDATAHEADER rdh;
|
||||||
|
char Buffer[1];
|
||||||
|
} RGNDATA,*PRGNDATA,*NPRGNDATA,*LPRGNDATA;
|
||||||
|
|
||||||
|
// Ignore these.
|
||||||
|
#define STDMETHODCALLTYPE
|
||||||
|
#define __stdcall
|
||||||
|
|
||||||
|
#define CONST const
|
||||||
|
#define CONST_VTBL const
|
||||||
|
|
||||||
|
#define TRUE 1
|
||||||
|
#define FALSE 0
|
||||||
|
|
||||||
|
#define WAIT_TIMEOUT 0x00000102
|
||||||
|
#define WAIT_FAILED 0xffffffff
|
||||||
|
#define WAIT_OBJECT_0 0
|
||||||
|
#define WAIT_ABANDONED 0x00000080
|
||||||
|
|
||||||
|
#define interface struct
|
||||||
|
#define MIDL_INTERFACE(x) struct
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \
|
||||||
|
constexpr GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}};
|
||||||
|
|
||||||
|
#define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k) \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<type*>() { return __uuidof_helper<type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<const type*>() { return __uuidof_helper<type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<type&>() { return __uuidof_helper<type>(); } } \
|
||||||
|
extern "C++" { template <> constexpr GUID __uuidof_helper<const type&>() { return __uuidof_helper<type>(); } }
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \
|
||||||
|
static const GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}};
|
||||||
|
#define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k)
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k)
|
||||||
|
|
||||||
|
#define S_OK 0
|
||||||
|
#define S_FALSE 1
|
||||||
|
|
||||||
|
#define E_INVALIDARG ((HRESULT)0x80070057)
|
||||||
|
#define E_FAIL ((HRESULT)0x80004005)
|
||||||
|
#define E_NOINTERFACE ((HRESULT)0x80004002)
|
||||||
|
#define E_NOTIMPL ((HRESULT)0x80004001)
|
||||||
|
#define E_OUTOFMEMORY ((HRESULT)0x8007000E)
|
||||||
|
#define E_POINTER ((HRESULT)0x80004003)
|
||||||
|
|
||||||
|
#define DXGI_STATUS_OCCLUDED ((HRESULT)0x087a0001)
|
||||||
|
#define DXGI_STATUS_CLIPPED ((HRESULT)0x087a0002)
|
||||||
|
#define DXGI_STATUS_NO_REDIRECTION ((HRESULT)0x087a0004)
|
||||||
|
#define DXGI_STATUS_NO_DESKTOP_ACCESS ((HRESULT)0x087a0005)
|
||||||
|
#define DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE ((HRESULT)0x087a0006)
|
||||||
|
#define DXGI_STATUS_MODE_CHANGED ((HRESULT)0x087a0007)
|
||||||
|
#define DXGI_STATUS_MODE_CHANGE_IN_PROGRESS ((HRESULT)0x087a0008)
|
||||||
|
#define DXGI_STATUS_UNOCCLUDED ((HRESULT)0x087a0009)
|
||||||
|
#define DXGI_STATUS_DDA_WAS_STILL_DRAWING ((HRESULT)0x087a000a)
|
||||||
|
#define DXGI_STATUS_PRESENT_REQUIRED ((HRESULT)0x087a002f)
|
||||||
|
|
||||||
|
#define DXGI_ERROR_INVALID_CALL ((HRESULT)0x887A0001)
|
||||||
|
#define DXGI_ERROR_NOT_FOUND ((HRESULT)0x887A0002)
|
||||||
|
#define DXGI_ERROR_MORE_DATA ((HRESULT)0x887A0003)
|
||||||
|
#define DXGI_ERROR_UNSUPPORTED ((HRESULT)0x887A0004)
|
||||||
|
#define DXGI_ERROR_DEVICE_REMOVED ((HRESULT)0x887A0005)
|
||||||
|
#define DXGI_ERROR_DEVICE_HUNG ((HRESULT)0x887A0006)
|
||||||
|
#define DXGI_ERROR_DEVICE_RESET ((HRESULT)0x887A0007)
|
||||||
|
#define DXGI_ERROR_WAS_STILL_DRAWING ((HRESULT)0x887A000A)
|
||||||
|
#define DXGI_ERROR_FRAME_STATISTICS_DISJOINT ((HRESULT)0x887A000B)
|
||||||
|
#define DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE ((HRESULT)0x887A000C)
|
||||||
|
#define DXGI_ERROR_DRIVER_INTERNAL_ERROR ((HRESULT)0x887A0020)
|
||||||
|
#define DXGI_ERROR_NONEXCLUSIVE ((HRESULT)0x887A0021)
|
||||||
|
#define DXGI_ERROR_NOT_CURRENTLY_AVAILABLE ((HRESULT)0x887A0022)
|
||||||
|
#define DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED ((HRESULT)0x887A0023)
|
||||||
|
#define DXGI_ERROR_REMOTE_OUTOFMEMORY ((HRESULT)0x887A0024)
|
||||||
|
#define DXGI_ERROR_ACCESS_LOST ((HRESULT)0x887A0026)
|
||||||
|
#define DXGI_ERROR_WAIT_TIMEOUT ((HRESULT)0x887A0027)
|
||||||
|
#define DXGI_ERROR_SESSION_DISCONNECTED ((HRESULT)0x887A0028)
|
||||||
|
#define DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE ((HRESULT)0x887A0029)
|
||||||
|
#define DXGI_ERROR_CANNOT_PROTECT_CONTENT ((HRESULT)0x887A002A)
|
||||||
|
#define DXGI_ERROR_ACCESS_DENIED ((HRESULT)0x887A002B)
|
||||||
|
#define DXGI_ERROR_NAME_ALREADY_EXISTS ((HRESULT)0x887A002C)
|
||||||
|
#define DXGI_ERROR_SDK_COMPONENT_MISSING ((HRESULT)0x887A002D)
|
||||||
|
|
||||||
|
#define WINAPI
|
||||||
|
#define WINUSERAPI
|
||||||
|
|
||||||
|
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
|
||||||
|
|
||||||
|
#define MAKE_HRESULT(sev,fac,code) \
|
||||||
|
((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define STDMETHOD(name) virtual HRESULT name
|
||||||
|
#define STDMETHOD_(type, name) virtual type name
|
||||||
|
#else
|
||||||
|
#define STDMETHOD(name) HRESULT (STDMETHODCALLTYPE *name)
|
||||||
|
#define STDMETHOD_(type, name) type (STDMETHODCALLTYPE *name)
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define THIS_
|
||||||
|
#define THIS
|
||||||
|
|
||||||
|
#define __C89_NAMELESSSTRUCTNAME
|
||||||
|
#define __C89_NAMELESSUNIONNAME
|
||||||
|
#define __C89_NAMELESSUNIONNAME1
|
||||||
|
#define __C89_NAMELESSUNIONNAME2
|
||||||
|
#define __C89_NAMELESSUNIONNAME3
|
||||||
|
#define __C89_NAMELESSUNIONNAME4
|
||||||
|
#define __C89_NAMELESSUNIONNAME5
|
||||||
|
#define __C89_NAMELESSUNIONNAME6
|
||||||
|
#define __C89_NAMELESSUNIONNAME7
|
||||||
|
#define __C89_NAMELESSUNIONNAME8
|
||||||
|
#define __C89_NAMELESS
|
||||||
|
#define DUMMYUNIONNAME
|
||||||
|
#define DUMMYSTRUCTNAME
|
||||||
|
#define DUMMYUNIONNAME1
|
||||||
|
#define DUMMYUNIONNAME2
|
||||||
|
#define DUMMYUNIONNAME3
|
||||||
|
#define DUMMYUNIONNAME4
|
||||||
|
#define DUMMYUNIONNAME5
|
||||||
|
#define DUMMYUNIONNAME6
|
||||||
|
#define DUMMYUNIONNAME7
|
||||||
|
#define DUMMYUNIONNAME8
|
||||||
|
#define DUMMYUNIONNAME9
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define DECLARE_INTERFACE(x) struct x
|
||||||
|
#define DECLARE_INTERFACE_(x, y) struct x : public y
|
||||||
|
#else
|
||||||
|
#define DECLARE_INTERFACE(x) \
|
||||||
|
typedef interface x { \
|
||||||
|
const struct x##Vtbl *lpVtbl; \
|
||||||
|
} x; \
|
||||||
|
typedef const struct x##Vtbl x##Vtbl; \
|
||||||
|
const struct x##Vtbl
|
||||||
|
#define DECLARE_INTERFACE_(x, y) DECLARE_INTERFACE(x)
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define BEGIN_INTERFACE
|
||||||
|
#define END_INTERFACE
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define PURE = 0
|
||||||
|
#else
|
||||||
|
#define PURE
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#define DECLSPEC_SELECTANY
|
||||||
|
|
||||||
|
#define __MSABI_LONG(x) x
|
||||||
|
|
||||||
|
#define ENUM_CURRENT_SETTINGS ((DWORD)-1)
|
||||||
|
#define ENUM_REGISTRY_SETTINGS ((DWORD)-2)
|
||||||
|
|
||||||
|
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
||||||
|
|
||||||
|
#define DUPLICATE_CLOSE_SOURCE ((DWORD)0x1)
|
||||||
|
#define DUPLICATE_SAME_ACCESS ((DWORD)0x2)
|
||||||
|
|
||||||
|
#define FAILED(hr) ((HRESULT)(hr) < 0)
|
||||||
|
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
|
||||||
|
|
||||||
|
#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))
|
||||||
|
#define ZeroMemory RtlZeroMemory
|
||||||
|
|
||||||
|
#ifndef DEFINE_ENUM_FLAG_OPERATORS
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define DEFINE_ENUM_FLAG_OPERATORS(type) \
|
||||||
|
extern "C++" \
|
||||||
|
{ \
|
||||||
|
inline type operator &(type x, type y) { return (type)((int)x & (int)y); } \
|
||||||
|
inline type operator &=(type &x, type y) { return (type &)((int &)x &= (int)y); } \
|
||||||
|
inline type operator ~(type x) { return (type)~(int)x; } \
|
||||||
|
inline type operator |(type x, type y) { return (type)((int)x | (int)y); } \
|
||||||
|
inline type operator |=(type &x, type y) { return (type &)((int &)x |= (int)y); } \
|
||||||
|
inline type operator ^(type x, type y) { return (type)((int)x ^ (int)y); } \
|
||||||
|
inline type operator ^=(type &x, type y) { return (type &)((int &)x ^= (int)y); } \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define DEFINE_ENUM_FLAG_OPERATORS(type)
|
||||||
|
#endif
|
||||||
|
#endif /* DEFINE_ENUM_FLAG_OPERATORS */
|
115
src/dxbc/dxbc_analysis.cpp
Normal file
115
src/dxbc/dxbc_analysis.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#include "dxbc_analysis.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcAnalyzer::DxbcAnalyzer(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const DxbcProgramInfo& programInfo,
|
||||||
|
const Rc<DxbcIsgn>& isgn,
|
||||||
|
const Rc<DxbcIsgn>& osgn,
|
||||||
|
const Rc<DxbcIsgn>& psgn,
|
||||||
|
DxbcAnalysisInfo& analysis)
|
||||||
|
: m_isgn (isgn),
|
||||||
|
m_osgn (osgn),
|
||||||
|
m_psgn (psgn),
|
||||||
|
m_analysis(&analysis) {
|
||||||
|
// Get number of clipping and culling planes from the
|
||||||
|
// input and output signatures. We will need this to
|
||||||
|
// declare the shader input and output interfaces.
|
||||||
|
m_analysis->clipCullIn = getClipCullInfo(m_isgn);
|
||||||
|
m_analysis->clipCullOut = getClipCullInfo(m_osgn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcAnalyzer::~DxbcAnalyzer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcAnalyzer::processInstruction(const DxbcShaderInstruction& ins) {
|
||||||
|
switch (ins.opClass) {
|
||||||
|
case DxbcInstClass::Atomic: {
|
||||||
|
const uint32_t operandId = ins.dstCount - 1;
|
||||||
|
|
||||||
|
if (ins.dst[operandId].type == DxbcOperandType::UnorderedAccessView) {
|
||||||
|
const uint32_t registerId = ins.dst[operandId].idx[0].offset;
|
||||||
|
m_analysis->uavInfos[registerId].accessAtomicOp = true;
|
||||||
|
m_analysis->uavInfos[registerId].accessFlags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::TextureSample:
|
||||||
|
case DxbcInstClass::TextureGather:
|
||||||
|
case DxbcInstClass::TextureQueryLod:
|
||||||
|
case DxbcInstClass::VectorDeriv: {
|
||||||
|
m_analysis->usesDerivatives = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::ControlFlow: {
|
||||||
|
if (ins.op == DxbcOpcode::Discard)
|
||||||
|
m_analysis->usesKill = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::BufferLoad: {
|
||||||
|
uint32_t operandId = ins.op == DxbcOpcode::LdStructured ? 2 : 1;
|
||||||
|
bool sparseFeedback = ins.dstCount == 2;
|
||||||
|
|
||||||
|
if (ins.src[operandId].type == DxbcOperandType::UnorderedAccessView) {
|
||||||
|
const uint32_t registerId = ins.src[operandId].idx[0].offset;
|
||||||
|
m_analysis->uavInfos[registerId].accessFlags |= VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
m_analysis->uavInfos[registerId].sparseFeedback |= sparseFeedback;
|
||||||
|
} else if (ins.src[operandId].type == DxbcOperandType::Resource) {
|
||||||
|
const uint32_t registerId = ins.src[operandId].idx[0].offset;
|
||||||
|
m_analysis->srvInfos[registerId].sparseFeedback |= sparseFeedback;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::BufferStore: {
|
||||||
|
if (ins.dst[0].type == DxbcOperandType::UnorderedAccessView) {
|
||||||
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
||||||
|
m_analysis->uavInfos[registerId].accessFlags |= VK_ACCESS_SHADER_WRITE_BIT;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::TypedUavLoad: {
|
||||||
|
const uint32_t registerId = ins.src[1].idx[0].offset;
|
||||||
|
m_analysis->uavInfos[registerId].accessTypedLoad = true;
|
||||||
|
m_analysis->uavInfos[registerId].accessFlags |= VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcInstClass::TypedUavStore: {
|
||||||
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
||||||
|
m_analysis->uavInfos[registerId].accessFlags |= VK_ACCESS_SHADER_WRITE_BIT;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < ins.dstCount; i++) {
|
||||||
|
if (ins.dst[0].type == DxbcOperandType::IndexableTemp) {
|
||||||
|
uint32_t index = ins.dst[0].idx[0].offset;
|
||||||
|
m_analysis->xRegMasks[index] |= ins.dst[0].mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcClipCullInfo DxbcAnalyzer::getClipCullInfo(const Rc<DxbcIsgn>& sgn) const {
|
||||||
|
DxbcClipCullInfo result;
|
||||||
|
|
||||||
|
if (sgn != nullptr) {
|
||||||
|
for (auto e = sgn->begin(); e != sgn->end(); e++) {
|
||||||
|
const uint32_t componentCount = e->componentMask.popCount();
|
||||||
|
|
||||||
|
if (e->systemValue == DxbcSystemValue::ClipDistance)
|
||||||
|
result.numClipPlanes += componentCount;
|
||||||
|
if (e->systemValue == DxbcSystemValue::CullDistance)
|
||||||
|
result.numCullPlanes += componentCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
100
src/dxbc/dxbc_analysis.h
Normal file
100
src/dxbc/dxbc_analysis.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_chunk_isgn.h"
|
||||||
|
#include "dxbc_decoder.h"
|
||||||
|
#include "dxbc_defs.h"
|
||||||
|
#include "dxbc_names.h"
|
||||||
|
#include "dxbc_modinfo.h"
|
||||||
|
#include "dxbc_util.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Info about unordered access views
|
||||||
|
*
|
||||||
|
* Stores whether an UAV is accessed with typed
|
||||||
|
* read or atomic instructions. This information
|
||||||
|
* will be used to generate image types.
|
||||||
|
*/
|
||||||
|
struct DxbcUavInfo {
|
||||||
|
bool accessTypedLoad = false;
|
||||||
|
bool accessAtomicOp = false;
|
||||||
|
bool sparseFeedback = false;
|
||||||
|
VkAccessFlags accessFlags = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Info about shader resource views
|
||||||
|
*
|
||||||
|
* Stores whether an SRV is accessed with
|
||||||
|
* sparse feedback. Useful for buffers.
|
||||||
|
*/
|
||||||
|
struct DxbcSrvInfo {
|
||||||
|
bool sparseFeedback = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Counts cull and clip distances
|
||||||
|
*/
|
||||||
|
struct DxbcClipCullInfo {
|
||||||
|
uint32_t numClipPlanes = 0;
|
||||||
|
uint32_t numCullPlanes = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader analysis info
|
||||||
|
*/
|
||||||
|
struct DxbcAnalysisInfo {
|
||||||
|
std::array<DxbcUavInfo, 64> uavInfos;
|
||||||
|
std::array<DxbcSrvInfo, 128> srvInfos;
|
||||||
|
std::array<DxbcRegMask, 4096> xRegMasks;
|
||||||
|
|
||||||
|
DxbcClipCullInfo clipCullIn;
|
||||||
|
DxbcClipCullInfo clipCullOut;
|
||||||
|
|
||||||
|
bool usesDerivatives = false;
|
||||||
|
bool usesKill = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC shader analysis pass
|
||||||
|
*
|
||||||
|
* Collects information about the shader itself
|
||||||
|
* and the resources used by the shader, which
|
||||||
|
* will later be used by the actual compiler.
|
||||||
|
*/
|
||||||
|
class DxbcAnalyzer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcAnalyzer(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const DxbcProgramInfo& programInfo,
|
||||||
|
const Rc<DxbcIsgn>& isgn,
|
||||||
|
const Rc<DxbcIsgn>& osgn,
|
||||||
|
const Rc<DxbcIsgn>& psgn,
|
||||||
|
DxbcAnalysisInfo& analysis);
|
||||||
|
|
||||||
|
~DxbcAnalyzer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Processes a single instruction
|
||||||
|
* \param [in] ins The instruction
|
||||||
|
*/
|
||||||
|
void processInstruction(
|
||||||
|
const DxbcShaderInstruction& ins);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Rc<DxbcIsgn> m_isgn;
|
||||||
|
Rc<DxbcIsgn> m_osgn;
|
||||||
|
Rc<DxbcIsgn> m_psgn;
|
||||||
|
|
||||||
|
DxbcAnalysisInfo* m_analysis = nullptr;
|
||||||
|
|
||||||
|
DxbcClipCullInfo getClipCullInfo(
|
||||||
|
const Rc<DxbcIsgn>& sgn) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
122
src/dxbc/dxbc_chunk_isgn.cpp
Normal file
122
src/dxbc/dxbc_chunk_isgn.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "dxbc_chunk_isgn.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcIsgn::DxbcIsgn(DxbcReader reader, DxbcTag tag) {
|
||||||
|
uint32_t elementCount = reader.readu32();
|
||||||
|
reader.skip(sizeof(uint32_t));
|
||||||
|
|
||||||
|
std::array<DxbcScalarType, 4> componentTypes = {
|
||||||
|
DxbcScalarType::Uint32, DxbcScalarType::Uint32,
|
||||||
|
DxbcScalarType::Sint32, DxbcScalarType::Float32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/DarkStarSword/3d-fixes/blob/master/dx11shaderanalyse.py#L101
|
||||||
|
bool hasStream = (tag == "ISG1") || (tag == "OSG1") || (tag == "PSG1") || (tag == "OSG5");
|
||||||
|
bool hasPrecision = (tag == "ISG1") || (tag == "OSG1") || (tag == "PSG1");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < elementCount; i++) {
|
||||||
|
DxbcSgnEntry entry;
|
||||||
|
entry.streamId = hasStream ? reader.readu32() : 0;
|
||||||
|
entry.semanticName = reader.clone(reader.readu32()).readString();
|
||||||
|
entry.semanticIndex = reader.readu32();
|
||||||
|
entry.systemValue = static_cast<DxbcSystemValue>(reader.readu32());
|
||||||
|
entry.componentType = componentTypes.at(reader.readu32());
|
||||||
|
entry.registerId = reader.readu32();
|
||||||
|
entry.componentMask = bit::extract(reader.readu32(), 0, 3);
|
||||||
|
|
||||||
|
if (hasPrecision)
|
||||||
|
reader.readu32();
|
||||||
|
|
||||||
|
m_entries.push_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcIsgn::~DxbcIsgn() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const DxbcSgnEntry* DxbcIsgn::findByRegister(uint32_t registerId) const {
|
||||||
|
for (auto e = this->begin(); e != this->end(); e++) {
|
||||||
|
if (e->registerId == registerId)
|
||||||
|
return &(*e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const DxbcSgnEntry* DxbcIsgn::find(
|
||||||
|
const std::string& semanticName,
|
||||||
|
uint32_t semanticIndex,
|
||||||
|
uint32_t streamId) const {
|
||||||
|
for (auto e = this->begin(); e != this->end(); e++) {
|
||||||
|
if (e->semanticIndex == semanticIndex
|
||||||
|
&& e->streamId == streamId
|
||||||
|
&& compareSemanticNames(semanticName, e->semanticName))
|
||||||
|
return &(*e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcRegMask DxbcIsgn::regMask(
|
||||||
|
uint32_t registerId) const {
|
||||||
|
DxbcRegMask mask;
|
||||||
|
|
||||||
|
for (auto e = this->begin(); e != this->end(); e++) {
|
||||||
|
if (e->registerId == registerId)
|
||||||
|
mask |= e->componentMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t DxbcIsgn::maxRegisterCount() const {
|
||||||
|
uint32_t result = 0;
|
||||||
|
for (auto e = this->begin(); e != this->end(); e++)
|
||||||
|
result = std::max(result, e->registerId + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxbcIsgn::printEntries() const {
|
||||||
|
for (auto entry = this->begin(); entry != this->end(); entry++) {
|
||||||
|
Logger::debug(str::format("SGN Entry:\n\t",
|
||||||
|
"semanticName: ", entry->semanticName, "\n\t",
|
||||||
|
"semanticIndex: ", entry->semanticIndex, "\n\t",
|
||||||
|
"registerId: ", entry->registerId, "\n\t",
|
||||||
|
"componentMask: ", entry->componentMask.maskString(), "\n\t",
|
||||||
|
"componentType: ", entry->componentType, "\n\t",
|
||||||
|
"systemValue: ", entry->systemValue, "\n\t",
|
||||||
|
"streamId: ", entry->streamId, "\n",
|
||||||
|
"\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DxbcIsgn::compareSemanticNames(
|
||||||
|
const std::string& a, const std::string& b) {
|
||||||
|
if (a.size() != b.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < a.size(); i++) {
|
||||||
|
char ac = a[i];
|
||||||
|
char bc = b[i];
|
||||||
|
|
||||||
|
if (ac != bc) {
|
||||||
|
if (ac >= 'A' && ac <= 'Z') ac += 'a' - 'A';
|
||||||
|
if (bc >= 'A' && bc <= 'Z') bc += 'a' - 'A';
|
||||||
|
|
||||||
|
if (ac != bc)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
src/dxbc/dxbc_chunk_isgn.h
Normal file
69
src/dxbc/dxbc_chunk_isgn.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
#include "dxbc_decoder.h"
|
||||||
|
#include "dxbc_enums.h"
|
||||||
|
#include "dxbc_reader.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Signature entry
|
||||||
|
*
|
||||||
|
* Stores the semantic name of an input or
|
||||||
|
* output and the corresponding register.
|
||||||
|
*/
|
||||||
|
struct DxbcSgnEntry {
|
||||||
|
std::string semanticName;
|
||||||
|
uint32_t semanticIndex;
|
||||||
|
uint32_t registerId;
|
||||||
|
DxbcRegMask componentMask;
|
||||||
|
DxbcScalarType componentType;
|
||||||
|
DxbcSystemValue systemValue;
|
||||||
|
uint32_t streamId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Input/Output signature chunk
|
||||||
|
*
|
||||||
|
* Stores information about the input and
|
||||||
|
* output registers used by the shader stage.
|
||||||
|
*/
|
||||||
|
class DxbcIsgn : public RcObject {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcIsgn(DxbcReader reader, DxbcTag tag);
|
||||||
|
~DxbcIsgn();
|
||||||
|
|
||||||
|
auto begin() const { return m_entries.cbegin(); }
|
||||||
|
auto end () const { return m_entries.cend(); }
|
||||||
|
|
||||||
|
const DxbcSgnEntry* findByRegister(
|
||||||
|
uint32_t registerId) const;
|
||||||
|
|
||||||
|
const DxbcSgnEntry* find(
|
||||||
|
const std::string& semanticName,
|
||||||
|
uint32_t semanticIndex,
|
||||||
|
uint32_t streamIndex) const;
|
||||||
|
|
||||||
|
DxbcRegMask regMask(
|
||||||
|
uint32_t registerId) const;
|
||||||
|
|
||||||
|
uint32_t maxRegisterCount() const;
|
||||||
|
|
||||||
|
void printEntries() const;
|
||||||
|
|
||||||
|
static bool compareSemanticNames(
|
||||||
|
const std::string& a,
|
||||||
|
const std::string& b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<DxbcSgnEntry> m_entries;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
24
src/dxbc/dxbc_chunk_shex.cpp
Normal file
24
src/dxbc/dxbc_chunk_shex.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "dxbc_chunk_shex.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcShex::DxbcShex(DxbcReader reader) {
|
||||||
|
// The shader version and type are stored in a 32-bit unit,
|
||||||
|
// where the first byte contains the major and minor version
|
||||||
|
// numbers, and the high word contains the program type.
|
||||||
|
reader.skip(2);
|
||||||
|
auto pType = reader.readEnum<DxbcProgramType>();
|
||||||
|
m_programInfo = DxbcProgramInfo(pType);
|
||||||
|
|
||||||
|
// Read the actual shader code as an array of DWORDs.
|
||||||
|
auto codeLength = reader.readu32() - 2;
|
||||||
|
m_code.resize(codeLength);
|
||||||
|
reader.read(m_code.data(), codeLength * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcShex::~DxbcShex() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/dxbc/dxbc_chunk_shex.h
Normal file
39
src/dxbc/dxbc_chunk_shex.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
#include "dxbc_decoder.h"
|
||||||
|
#include "dxbc_reader.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader code chunk
|
||||||
|
*
|
||||||
|
* Stores the DXBC shader code itself, as well
|
||||||
|
* as some meta info about the shader, i.e. what
|
||||||
|
* type of shader this is.
|
||||||
|
*/
|
||||||
|
class DxbcShex : public RcObject {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcShex(DxbcReader reader);
|
||||||
|
~DxbcShex();
|
||||||
|
|
||||||
|
DxbcProgramInfo programInfo() const {
|
||||||
|
return m_programInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcCodeSlice slice() const {
|
||||||
|
return DxbcCodeSlice(m_code.data(),
|
||||||
|
m_code.data() + m_code.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcProgramInfo m_programInfo;
|
||||||
|
std::vector<uint32_t> m_code;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
32
src/dxbc/dxbc_common.cpp
Normal file
32
src/dxbc/dxbc_common.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
VkShaderStageFlagBits DxbcProgramInfo::shaderStage() const {
|
||||||
|
switch (m_type) {
|
||||||
|
case DxbcProgramType::PixelShader : return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
case DxbcProgramType::VertexShader : return VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
case DxbcProgramType::GeometryShader : return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||||
|
case DxbcProgramType::HullShader : return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||||
|
case DxbcProgramType::DomainShader : return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||||
|
case DxbcProgramType::ComputeShader : return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw DxvkError("DxbcProgramInfo::shaderStage: Unsupported program type");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
spv::ExecutionModel DxbcProgramInfo::executionModel() const {
|
||||||
|
switch (m_type) {
|
||||||
|
case DxbcProgramType::PixelShader : return spv::ExecutionModelFragment;
|
||||||
|
case DxbcProgramType::VertexShader : return spv::ExecutionModelVertex;
|
||||||
|
case DxbcProgramType::GeometryShader : return spv::ExecutionModelGeometry;
|
||||||
|
case DxbcProgramType::HullShader : return spv::ExecutionModelTessellationControl;
|
||||||
|
case DxbcProgramType::DomainShader : return spv::ExecutionModelTessellationEvaluation;
|
||||||
|
case DxbcProgramType::ComputeShader : return spv::ExecutionModelGLCompute;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw DxvkError("DxbcProgramInfo::executionModel: Unsupported program type");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
src/dxbc/dxbc_common.h
Normal file
69
src/dxbc/dxbc_common.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_include.h"
|
||||||
|
#include <spirv/unified1/spirv.hpp>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC Program type
|
||||||
|
*
|
||||||
|
* Defines the shader stage that a DXBC
|
||||||
|
* module has been compiled form.
|
||||||
|
*/
|
||||||
|
enum class DxbcProgramType : uint16_t {
|
||||||
|
PixelShader = 0,
|
||||||
|
VertexShader = 1,
|
||||||
|
GeometryShader = 2,
|
||||||
|
HullShader = 3,
|
||||||
|
DomainShader = 4,
|
||||||
|
ComputeShader = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC shader info
|
||||||
|
*
|
||||||
|
* Stores the shader program type.
|
||||||
|
*/
|
||||||
|
class DxbcProgramInfo {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcProgramInfo() { }
|
||||||
|
DxbcProgramInfo(DxbcProgramType type)
|
||||||
|
: m_type(type) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Program type
|
||||||
|
* \returns Program type
|
||||||
|
*/
|
||||||
|
DxbcProgramType type() const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Vulkan shader stage
|
||||||
|
*
|
||||||
|
* The \c VkShaderStageFlagBits constant
|
||||||
|
* that corresponds to the program type.
|
||||||
|
* \returns Vulkan shaer stage
|
||||||
|
*/
|
||||||
|
VkShaderStageFlagBits shaderStage() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SPIR-V execution model
|
||||||
|
*
|
||||||
|
* The execution model that corresponds
|
||||||
|
* to the Vulkan shader stage.
|
||||||
|
* \returns SPIR-V execution model
|
||||||
|
*/
|
||||||
|
spv::ExecutionModel executionModel() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcProgramType m_type = DxbcProgramType::PixelShader;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
7937
src/dxbc/dxbc_compiler.cpp
Normal file
7937
src/dxbc/dxbc_compiler.cpp
Normal file
File diff suppressed because it is too large
Load diff
1276
src/dxbc/dxbc_compiler.h
Normal file
1276
src/dxbc/dxbc_compiler.h
Normal file
File diff suppressed because it is too large
Load diff
360
src/dxbc/dxbc_decoder.cpp
Normal file
360
src/dxbc/dxbc_decoder.cpp
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
#include "dxbc_decoder.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
const uint32_t* DxbcCodeSlice::ptrAt(uint32_t id) const {
|
||||||
|
if (m_ptr + id >= m_end)
|
||||||
|
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||||
|
return m_ptr + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t DxbcCodeSlice::at(uint32_t id) const {
|
||||||
|
if (m_ptr + id >= m_end)
|
||||||
|
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||||
|
return m_ptr[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t DxbcCodeSlice::read() {
|
||||||
|
if (m_ptr >= m_end)
|
||||||
|
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||||
|
return *(m_ptr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcCodeSlice DxbcCodeSlice::take(uint32_t n) const {
|
||||||
|
if (m_ptr + n > m_end)
|
||||||
|
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||||
|
return DxbcCodeSlice(m_ptr, m_ptr + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcCodeSlice DxbcCodeSlice::skip(uint32_t n) const {
|
||||||
|
if (m_ptr + n > m_end)
|
||||||
|
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||||
|
return DxbcCodeSlice(m_ptr + n, m_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeInstruction(DxbcCodeSlice& code) {
|
||||||
|
const uint32_t token0 = code.at(0);
|
||||||
|
|
||||||
|
// Initialize the instruction structure. Some of these values
|
||||||
|
// may not get written otherwise while decoding the instruction.
|
||||||
|
m_instruction.op = static_cast<DxbcOpcode>(bit::extract(token0, 0, 10));
|
||||||
|
m_instruction.opClass = DxbcInstClass::Undefined;
|
||||||
|
m_instruction.sampleControls = { 0, 0, 0 };
|
||||||
|
m_instruction.dstCount = 0;
|
||||||
|
m_instruction.srcCount = 0;
|
||||||
|
m_instruction.immCount = 0;
|
||||||
|
m_instruction.dst = m_dstOperands.data();
|
||||||
|
m_instruction.src = m_srcOperands.data();
|
||||||
|
m_instruction.imm = m_immOperands.data();
|
||||||
|
m_instruction.customDataType = DxbcCustomDataClass::Comment;
|
||||||
|
m_instruction.customDataSize = 0;
|
||||||
|
m_instruction.customData = nullptr;
|
||||||
|
|
||||||
|
// Reset the index pointer, which may still contain
|
||||||
|
// a non-zero value from the previous iteration
|
||||||
|
m_indexId = 0;
|
||||||
|
|
||||||
|
// Instruction length, in DWORDs. This includes the token
|
||||||
|
// itself and any other prefix that an instruction may have.
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
if (m_instruction.op == DxbcOpcode::CustomData) {
|
||||||
|
length = code.at(1);
|
||||||
|
this->decodeCustomData(code.take(length));
|
||||||
|
} else {
|
||||||
|
length = bit::extract(token0, 24, 30);
|
||||||
|
this->decodeOperation(code.take(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the caller's slice to the next token so that
|
||||||
|
// they can make consecutive calls to decodeInstruction()
|
||||||
|
code = code.skip(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeCustomData(DxbcCodeSlice code) {
|
||||||
|
const uint32_t blockLength = code.at(1);
|
||||||
|
|
||||||
|
if (blockLength < 2) {
|
||||||
|
Logger::err("DxbcDecodeContext: Invalid custom data block");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom data blocks have their own instruction class
|
||||||
|
m_instruction.op = DxbcOpcode::CustomData;
|
||||||
|
m_instruction.opClass = DxbcInstClass::CustomData;
|
||||||
|
|
||||||
|
// We'll point into the code buffer rather than making a copy
|
||||||
|
m_instruction.customDataType = static_cast<DxbcCustomDataClass>(
|
||||||
|
bit::extract(code.at(0), 11, 31));
|
||||||
|
m_instruction.customDataSize = blockLength - 2;
|
||||||
|
m_instruction.customData = code.ptrAt(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeOperation(DxbcCodeSlice code) {
|
||||||
|
uint32_t token = code.read();
|
||||||
|
|
||||||
|
// Result modifiers, which are applied to common ALU ops
|
||||||
|
m_instruction.modifiers.saturate = !!bit::extract(token, 13, 13);
|
||||||
|
m_instruction.modifiers.precise = !!bit::extract(token, 19, 22);
|
||||||
|
|
||||||
|
// Opcode controls. It will depend on the
|
||||||
|
// opcode itself which ones are valid.
|
||||||
|
m_instruction.controls = DxbcShaderOpcodeControls(token);
|
||||||
|
|
||||||
|
// Process extended opcode tokens
|
||||||
|
while (bit::extract(token, 31, 31)) {
|
||||||
|
token = code.read();
|
||||||
|
|
||||||
|
const DxbcExtOpcode extOpcode
|
||||||
|
= static_cast<DxbcExtOpcode>(bit::extract(token, 0, 5));
|
||||||
|
|
||||||
|
switch (extOpcode) {
|
||||||
|
case DxbcExtOpcode::SampleControls: {
|
||||||
|
struct {
|
||||||
|
int u : 4;
|
||||||
|
int v : 4;
|
||||||
|
int w : 4;
|
||||||
|
} aoffimmi;
|
||||||
|
|
||||||
|
aoffimmi.u = bit::extract(token, 9, 12);
|
||||||
|
aoffimmi.v = bit::extract(token, 13, 16);
|
||||||
|
aoffimmi.w = bit::extract(token, 17, 20);
|
||||||
|
|
||||||
|
// Four-bit signed numbers, sign-extend them
|
||||||
|
m_instruction.sampleControls.u = aoffimmi.u;
|
||||||
|
m_instruction.sampleControls.v = aoffimmi.v;
|
||||||
|
m_instruction.sampleControls.w = aoffimmi.w;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcExtOpcode::ResourceDim:
|
||||||
|
case DxbcExtOpcode::ResourceReturnType:
|
||||||
|
break; // part of resource description
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn(str::format(
|
||||||
|
"DxbcDecodeContext: Unhandled extended opcode: ",
|
||||||
|
extOpcode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the instruction format in order to parse the
|
||||||
|
// operands. Doing this mostly automatically means that
|
||||||
|
// the compiler can rely on the operands being valid.
|
||||||
|
const DxbcInstFormat format = dxbcInstructionFormat(m_instruction.op);
|
||||||
|
m_instruction.opClass = format.instructionClass;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < format.operandCount; i++)
|
||||||
|
this->decodeOperand(code, format.operands[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeComponentSelection(DxbcRegister& reg, uint32_t token) {
|
||||||
|
// Pick the correct component selection mode based on the
|
||||||
|
// component count. We'll simplify this here so that the
|
||||||
|
// compiler can assume that everything is a 4D vector.
|
||||||
|
reg.componentCount = static_cast<DxbcComponentCount>(bit::extract(token, 0, 1));
|
||||||
|
|
||||||
|
switch (reg.componentCount) {
|
||||||
|
// No components - used for samplers etc.
|
||||||
|
case DxbcComponentCount::Component0:
|
||||||
|
reg.mask = DxbcRegMask(false, false, false, false);
|
||||||
|
reg.swizzle = DxbcRegSwizzle(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// One component - used for immediates
|
||||||
|
// and a few built-in registers.
|
||||||
|
case DxbcComponentCount::Component1:
|
||||||
|
reg.mask = DxbcRegMask(true, false, false, false);
|
||||||
|
reg.swizzle = DxbcRegSwizzle(0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Four components - everything else. This requires us
|
||||||
|
// to actually parse the component selection mode.
|
||||||
|
case DxbcComponentCount::Component4: {
|
||||||
|
const DxbcRegMode componentMode =
|
||||||
|
static_cast<DxbcRegMode>(bit::extract(token, 2, 3));
|
||||||
|
|
||||||
|
switch (componentMode) {
|
||||||
|
// Write mask for destination operands
|
||||||
|
case DxbcRegMode::Mask:
|
||||||
|
reg.mask = bit::extract(token, 4, 7);
|
||||||
|
reg.swizzle = DxbcRegSwizzle(0, 1, 2, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Swizzle for source operands (including resources)
|
||||||
|
case DxbcRegMode::Swizzle:
|
||||||
|
reg.mask = DxbcRegMask(true, true, true, true);
|
||||||
|
reg.swizzle = DxbcRegSwizzle(
|
||||||
|
bit::extract(token, 4, 5),
|
||||||
|
bit::extract(token, 6, 7),
|
||||||
|
bit::extract(token, 8, 9),
|
||||||
|
bit::extract(token, 10, 11));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Selection of one component. We can generate both a
|
||||||
|
// mask and a swizzle for this so that the compiler
|
||||||
|
// won't have to deal with this case specifically.
|
||||||
|
case DxbcRegMode::Select1: {
|
||||||
|
const uint32_t n = bit::extract(token, 4, 5);
|
||||||
|
reg.mask = DxbcRegMask(n == 0, n == 1, n == 2, n == 3);
|
||||||
|
reg.swizzle = DxbcRegSwizzle(n, n, n, n);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn("DxbcDecodeContext: Invalid component selection mode");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn("DxbcDecodeContext: Invalid component count");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeOperandExtensions(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token) {
|
||||||
|
while (bit::extract(token, 31, 31)) {
|
||||||
|
token = code.read();
|
||||||
|
|
||||||
|
// Type of the extended operand token
|
||||||
|
const DxbcOperandExt extTokenType =
|
||||||
|
static_cast<DxbcOperandExt>(bit::extract(token, 0, 5));
|
||||||
|
|
||||||
|
switch (extTokenType) {
|
||||||
|
// Operand modifiers, which are used to manipulate the
|
||||||
|
// value of a source operand during the load operation
|
||||||
|
case DxbcOperandExt::OperandModifier:
|
||||||
|
reg.modifiers = bit::extract(token, 6, 13);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn(str::format(
|
||||||
|
"DxbcDecodeContext: Unhandled extended operand token: ",
|
||||||
|
extTokenType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeOperandImmediates(DxbcCodeSlice& code, DxbcRegister& reg) {
|
||||||
|
if (reg.type == DxbcOperandType::Imm32
|
||||||
|
|| reg.type == DxbcOperandType::Imm64) {
|
||||||
|
switch (reg.componentCount) {
|
||||||
|
// This is commonly used if only one vector
|
||||||
|
// component is involved in an operation
|
||||||
|
case DxbcComponentCount::Component1: {
|
||||||
|
reg.imm.u32_1 = code.read();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Typical four-component vector
|
||||||
|
case DxbcComponentCount::Component4: {
|
||||||
|
reg.imm.u32_4[0] = code.read();
|
||||||
|
reg.imm.u32_4[1] = code.read();
|
||||||
|
reg.imm.u32_4[2] = code.read();
|
||||||
|
reg.imm.u32_4[3] = code.read();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn("DxbcDecodeContext: Invalid component count for immediate operand");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeOperandIndex(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token) {
|
||||||
|
reg.idxDim = bit::extract(token, 20, 21);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < reg.idxDim; i++) {
|
||||||
|
// An index can be encoded in various different ways
|
||||||
|
const DxbcOperandIndexRepresentation repr =
|
||||||
|
static_cast<DxbcOperandIndexRepresentation>(
|
||||||
|
bit::extract(token, 22 + 3 * i, 24 + 3 * i));
|
||||||
|
|
||||||
|
switch (repr) {
|
||||||
|
case DxbcOperandIndexRepresentation::Imm32:
|
||||||
|
reg.idx[i].offset = static_cast<int32_t>(code.read());
|
||||||
|
reg.idx[i].relReg = nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DxbcOperandIndexRepresentation::Relative:
|
||||||
|
reg.idx[i].offset = 0;
|
||||||
|
reg.idx[i].relReg = &m_indices.at(m_indexId);
|
||||||
|
|
||||||
|
this->decodeRegister(code,
|
||||||
|
m_indices.at(m_indexId++),
|
||||||
|
DxbcScalarType::Sint32);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DxbcOperandIndexRepresentation::Imm32Relative:
|
||||||
|
reg.idx[i].offset = static_cast<int32_t>(code.read());
|
||||||
|
reg.idx[i].relReg = &m_indices.at(m_indexId);
|
||||||
|
|
||||||
|
this->decodeRegister(code,
|
||||||
|
m_indices.at(m_indexId++),
|
||||||
|
DxbcScalarType::Sint32);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger::warn(str::format(
|
||||||
|
"DxbcDecodeContext: Unhandled index representation: ",
|
||||||
|
repr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeRegister(DxbcCodeSlice& code, DxbcRegister& reg, DxbcScalarType type) {
|
||||||
|
const uint32_t token = code.read();
|
||||||
|
|
||||||
|
reg.type = static_cast<DxbcOperandType>(bit::extract(token, 12, 19));
|
||||||
|
reg.dataType = type;
|
||||||
|
reg.modifiers = 0;
|
||||||
|
reg.idxDim = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < DxbcMaxRegIndexDim; i++) {
|
||||||
|
reg.idx[i].relReg = nullptr;
|
||||||
|
reg.idx[i].offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->decodeComponentSelection(reg, token);
|
||||||
|
this->decodeOperandExtensions(code, reg, token);
|
||||||
|
this->decodeOperandImmediates(code, reg);
|
||||||
|
this->decodeOperandIndex(code, reg, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeImm32(DxbcCodeSlice& code, DxbcImmediate& imm, DxbcScalarType type) {
|
||||||
|
imm.u32 = code.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcDecodeContext::decodeOperand(DxbcCodeSlice& code, const DxbcInstOperandFormat& format) {
|
||||||
|
switch (format.kind) {
|
||||||
|
case DxbcOperandKind::DstReg: {
|
||||||
|
const uint32_t operandId = m_instruction.dstCount++;
|
||||||
|
this->decodeRegister(code, m_dstOperands.at(operandId), format.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcOperandKind::SrcReg: {
|
||||||
|
const uint32_t operandId = m_instruction.srcCount++;
|
||||||
|
this->decodeRegister(code, m_srcOperands.at(operandId), format.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DxbcOperandKind::Imm32: {
|
||||||
|
const uint32_t operandId = m_instruction.immCount++;
|
||||||
|
this->decodeImm32(code, m_immOperands.at(operandId), format.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw DxvkError("DxbcDecodeContext: Invalid operand format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
505
src/dxbc/dxbc_decoder.h
Normal file
505
src/dxbc/dxbc_decoder.h
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
#include "dxbc_decoder.h"
|
||||||
|
#include "dxbc_defs.h"
|
||||||
|
#include "dxbc_enums.h"
|
||||||
|
#include "dxbc_names.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
constexpr size_t DxbcMaxRegIndexDim = 3;
|
||||||
|
|
||||||
|
struct DxbcRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Source operand modifiers
|
||||||
|
*
|
||||||
|
* These are applied after loading
|
||||||
|
* an operand register.
|
||||||
|
*/
|
||||||
|
enum class DxbcRegModifier : uint32_t {
|
||||||
|
Neg = 0,
|
||||||
|
Abs = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxbcRegModifiers = Flags<DxbcRegModifier>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constant buffer binding
|
||||||
|
*
|
||||||
|
* Stores information required to
|
||||||
|
* access a constant buffer.
|
||||||
|
*/
|
||||||
|
struct DxbcConstantBuffer {
|
||||||
|
uint32_t varId = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sampler binding
|
||||||
|
*
|
||||||
|
* Stores a sampler variable that can be
|
||||||
|
* used together with a texture resource.
|
||||||
|
*/
|
||||||
|
struct DxbcSampler {
|
||||||
|
uint32_t varId = 0;
|
||||||
|
uint32_t typeId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Image type information
|
||||||
|
*/
|
||||||
|
struct DxbcImageInfo {
|
||||||
|
spv::Dim dim = spv::Dim1D;
|
||||||
|
uint32_t array = 0;
|
||||||
|
uint32_t ms = 0;
|
||||||
|
uint32_t sampled = 0;
|
||||||
|
VkImageViewType vtype = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader resource binding
|
||||||
|
*
|
||||||
|
* Stores a resource variable
|
||||||
|
* and associated type IDs.
|
||||||
|
*/
|
||||||
|
struct DxbcShaderResource {
|
||||||
|
DxbcResourceType type = DxbcResourceType::Typed;
|
||||||
|
DxbcImageInfo imageInfo;
|
||||||
|
uint32_t varId = 0;
|
||||||
|
uint32_t specId = 0;
|
||||||
|
DxbcScalarType sampledType = DxbcScalarType::Float32;
|
||||||
|
uint32_t sampledTypeId = 0;
|
||||||
|
uint32_t imageTypeId = 0;
|
||||||
|
uint32_t colorTypeId = 0;
|
||||||
|
uint32_t depthTypeId = 0;
|
||||||
|
uint32_t structStride = 0;
|
||||||
|
bool isRawSsbo = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Unordered access binding
|
||||||
|
*
|
||||||
|
* Stores a resource variable that is provided
|
||||||
|
* by a UAV, as well as associated type IDs.
|
||||||
|
*/
|
||||||
|
struct DxbcUav {
|
||||||
|
DxbcResourceType type = DxbcResourceType::Typed;
|
||||||
|
DxbcImageInfo imageInfo;
|
||||||
|
uint32_t varId = 0;
|
||||||
|
uint32_t ctrId = 0;
|
||||||
|
uint32_t specId = 0;
|
||||||
|
DxbcScalarType sampledType = DxbcScalarType::Float32;
|
||||||
|
uint32_t sampledTypeId = 0;
|
||||||
|
uint32_t imageTypeId = 0;
|
||||||
|
uint32_t structStride = 0;
|
||||||
|
uint32_t coherence = 0;
|
||||||
|
bool isRawSsbo = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Component swizzle
|
||||||
|
*
|
||||||
|
* Maps vector components to
|
||||||
|
* other vector components.
|
||||||
|
*/
|
||||||
|
class DxbcRegSwizzle {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcRegSwizzle() { }
|
||||||
|
DxbcRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
|
||||||
|
: m_mask((x << 0) | (y << 2) | (z << 4) | (w << 6)) { }
|
||||||
|
|
||||||
|
uint32_t operator [] (uint32_t id) const {
|
||||||
|
return (m_mask >> (id + id)) & 0x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const DxbcRegSwizzle& other) const { return m_mask == other.m_mask; }
|
||||||
|
bool operator != (const DxbcRegSwizzle& other) const { return m_mask != other.m_mask; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint8_t m_mask = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Component mask
|
||||||
|
*
|
||||||
|
* Enables access to certain
|
||||||
|
* subset of vector components.
|
||||||
|
*/
|
||||||
|
class DxbcRegMask {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcRegMask() { }
|
||||||
|
DxbcRegMask(uint32_t mask) : m_mask(mask) { }
|
||||||
|
DxbcRegMask(bool x, bool y, bool z, bool w)
|
||||||
|
: m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0)
|
||||||
|
| (z ? 0x4 : 0) | (w ? 0x8 : 0)) { }
|
||||||
|
|
||||||
|
bool operator [] (uint32_t id) const {
|
||||||
|
return (m_mask >> id) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t popCount() const {
|
||||||
|
const uint8_t n[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4 };
|
||||||
|
return n[m_mask & 0xF];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t firstSet() const {
|
||||||
|
const uint8_t n[16] = { 4, 0, 1, 0, 2, 0, 1, 0,
|
||||||
|
3, 0, 1, 0, 2, 0, 1, 0 };
|
||||||
|
return n[m_mask & 0xF];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t minComponents() const {
|
||||||
|
const uint8_t n[16] = { 0, 1, 2, 2, 3, 3, 3, 3,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4 };
|
||||||
|
return n[m_mask & 0xF];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const DxbcRegMask& other) const { return m_mask == other.m_mask; }
|
||||||
|
bool operator != (const DxbcRegMask& other) const { return m_mask != other.m_mask; }
|
||||||
|
|
||||||
|
DxbcRegMask& operator |= (const DxbcRegMask& other) {
|
||||||
|
m_mask |= other.m_mask;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DxbcRegMask firstN(uint32_t n) {
|
||||||
|
return DxbcRegMask(n >= 1, n >= 2, n >= 3, n >= 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DxbcRegMask select(uint32_t n) {
|
||||||
|
return DxbcRegMask(n == 0, n == 1, n == 2, n == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string maskString() const {
|
||||||
|
std::string out = "";
|
||||||
|
out += (m_mask & 0x1) ? "x" : "";
|
||||||
|
out += (m_mask & 0x2) ? "y" : "";
|
||||||
|
out += (m_mask & 0x4) ? "z" : "";
|
||||||
|
out += (m_mask & 0x8) ? "w" : "";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool () const {
|
||||||
|
return m_mask != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint8_t m_mask = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief System value mapping
|
||||||
|
*
|
||||||
|
* Maps a system value to a given set of
|
||||||
|
* components of an input or output register.
|
||||||
|
*/
|
||||||
|
struct DxbcSvMapping {
|
||||||
|
uint32_t regId;
|
||||||
|
DxbcRegMask regMask;
|
||||||
|
DxbcSystemValue sv;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct DxbcRegIndex {
|
||||||
|
DxbcRegister* relReg;
|
||||||
|
int32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction operand
|
||||||
|
*/
|
||||||
|
struct DxbcRegister {
|
||||||
|
DxbcOperandType type;
|
||||||
|
DxbcScalarType dataType;
|
||||||
|
DxbcComponentCount componentCount;
|
||||||
|
|
||||||
|
uint32_t idxDim;
|
||||||
|
DxbcRegIndex idx[DxbcMaxRegIndexDim];
|
||||||
|
|
||||||
|
DxbcRegMask mask;
|
||||||
|
DxbcRegSwizzle swizzle;
|
||||||
|
DxbcRegModifiers modifiers;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint32_t u32_4[4];
|
||||||
|
uint32_t u32_1;
|
||||||
|
} imm;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction result modifiers
|
||||||
|
*
|
||||||
|
* Modifiers that are applied
|
||||||
|
* to all destination operands.
|
||||||
|
*/
|
||||||
|
struct DxbcOpModifiers {
|
||||||
|
bool saturate;
|
||||||
|
bool precise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Opcode controls
|
||||||
|
*
|
||||||
|
* Instruction-specific controls. Usually,
|
||||||
|
* only one of the members will be valid.
|
||||||
|
*/
|
||||||
|
class DxbcShaderOpcodeControls {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcShaderOpcodeControls()
|
||||||
|
: m_bits(0) { }
|
||||||
|
|
||||||
|
DxbcShaderOpcodeControls(uint32_t bits)
|
||||||
|
: m_bits(bits) { }
|
||||||
|
|
||||||
|
DxbcInstructionReturnType returnType() const {
|
||||||
|
return DxbcInstructionReturnType(bit::extract(m_bits, 11, 11));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcGlobalFlags globalFlags() const {
|
||||||
|
return DxbcGlobalFlags(bit::extract(m_bits, 11, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcZeroTest zeroTest() const {
|
||||||
|
return DxbcZeroTest(bit::extract(m_bits, 18, 18));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcSyncFlags syncFlags() const {
|
||||||
|
return DxbcSyncFlags(bit::extract(m_bits, 11, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcResourceDim resourceDim() const {
|
||||||
|
return DxbcResourceDim(bit::extract(m_bits, 11, 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcResinfoType resinfoType() const {
|
||||||
|
return DxbcResinfoType(bit::extract(m_bits, 11, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcInterpolationMode interpolation() const {
|
||||||
|
return DxbcInterpolationMode(bit::extract(m_bits, 11, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcSamplerMode samplerMode() const {
|
||||||
|
return DxbcSamplerMode(bit::extract(m_bits, 11, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcPrimitiveTopology primitiveTopology() const {
|
||||||
|
return DxbcPrimitiveTopology(bit::extract(m_bits, 11, 17));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcPrimitive primitive() const {
|
||||||
|
return DxbcPrimitive(bit::extract(m_bits, 11, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcTessDomain tessDomain() const {
|
||||||
|
return DxbcTessDomain(bit::extract(m_bits, 11, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcTessOutputPrimitive tessOutputPrimitive() const {
|
||||||
|
return DxbcTessOutputPrimitive(bit::extract(m_bits, 11, 13));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcTessPartitioning tessPartitioning() const {
|
||||||
|
return DxbcTessPartitioning(bit::extract(m_bits, 11, 13));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcUavFlags uavFlags() const {
|
||||||
|
return DxbcUavFlags(bit::extract(m_bits, 16, 17));
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcConstantBufferAccessType accessType() const {
|
||||||
|
return DxbcConstantBufferAccessType(bit::extract(m_bits, 11, 11));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t controlPointCount() const {
|
||||||
|
return bit::extract(m_bits, 11, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool precise() const {
|
||||||
|
return bit::extract(m_bits, 19, 22) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t m_bits;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sample controls
|
||||||
|
*
|
||||||
|
* Constant texel offset with
|
||||||
|
* values raning from -8 to 7.
|
||||||
|
*/
|
||||||
|
struct DxbcShaderSampleControls {
|
||||||
|
int u, v, w;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Immediate value
|
||||||
|
*
|
||||||
|
* Immediate argument represented either
|
||||||
|
* as a 32-bit or 64-bit unsigned integer,
|
||||||
|
* or a 32-bit or 32-bit floating point number.
|
||||||
|
*/
|
||||||
|
union DxbcImmediate {
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
uint32_t u32;
|
||||||
|
uint64_t u64;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader instruction
|
||||||
|
*
|
||||||
|
* Note that this structure may store pointer to
|
||||||
|
* external structures, such as the original code
|
||||||
|
* buffer. This is safe to use if and only if:
|
||||||
|
* - The \ref DxbcDecodeContext that created it
|
||||||
|
* still exists and was not moved
|
||||||
|
* - The code buffer that was being decoded
|
||||||
|
* still exists and was not moved.
|
||||||
|
*/
|
||||||
|
struct DxbcShaderInstruction {
|
||||||
|
DxbcOpcode op;
|
||||||
|
DxbcInstClass opClass;
|
||||||
|
DxbcOpModifiers modifiers;
|
||||||
|
DxbcShaderOpcodeControls controls;
|
||||||
|
DxbcShaderSampleControls sampleControls;
|
||||||
|
|
||||||
|
uint32_t dstCount;
|
||||||
|
uint32_t srcCount;
|
||||||
|
uint32_t immCount;
|
||||||
|
|
||||||
|
const DxbcRegister* dst;
|
||||||
|
const DxbcRegister* src;
|
||||||
|
const DxbcImmediate* imm;
|
||||||
|
|
||||||
|
DxbcCustomDataClass customDataType;
|
||||||
|
uint32_t customDataSize;
|
||||||
|
const uint32_t* customData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC code slice
|
||||||
|
*
|
||||||
|
* Convenient pointer pair that allows
|
||||||
|
* reading the code word stream safely.
|
||||||
|
*/
|
||||||
|
class DxbcCodeSlice {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcCodeSlice(
|
||||||
|
const uint32_t* ptr,
|
||||||
|
const uint32_t* end)
|
||||||
|
: m_ptr(ptr), m_end(end) { }
|
||||||
|
|
||||||
|
const uint32_t* ptrAt(uint32_t id) const;
|
||||||
|
|
||||||
|
uint32_t at(uint32_t id) const;
|
||||||
|
uint32_t read();
|
||||||
|
|
||||||
|
DxbcCodeSlice take(uint32_t n) const;
|
||||||
|
DxbcCodeSlice skip(uint32_t n) const;
|
||||||
|
|
||||||
|
bool atEnd() const {
|
||||||
|
return m_ptr == m_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const uint32_t* m_ptr = nullptr;
|
||||||
|
const uint32_t* m_end = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode context
|
||||||
|
*
|
||||||
|
* Stores data that is required to decode a single
|
||||||
|
* instruction. This data is not persistent, so it
|
||||||
|
* should be forwarded to the compiler right away.
|
||||||
|
*/
|
||||||
|
class DxbcDecodeContext {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves current instruction
|
||||||
|
*
|
||||||
|
* This is only valid after a call to \ref decode.
|
||||||
|
* \returns Reference to last decoded instruction
|
||||||
|
*/
|
||||||
|
const DxbcShaderInstruction& getInstruction() const {
|
||||||
|
return m_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decodes an instruction
|
||||||
|
*
|
||||||
|
* This also advances the given code slice by the
|
||||||
|
* number of dwords consumed by the instruction.
|
||||||
|
* \param [in] code Code slice
|
||||||
|
*/
|
||||||
|
void decodeInstruction(DxbcCodeSlice& code);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcShaderInstruction m_instruction;
|
||||||
|
|
||||||
|
std::array<DxbcRegister, 8> m_dstOperands;
|
||||||
|
std::array<DxbcRegister, 8> m_srcOperands;
|
||||||
|
std::array<DxbcImmediate, 4> m_immOperands;
|
||||||
|
std::array<DxbcRegister, 12> m_indices;
|
||||||
|
|
||||||
|
// Index into the indices array. Used when decoding
|
||||||
|
// instruction operands with relative indexing.
|
||||||
|
uint32_t m_indexId = 0;
|
||||||
|
|
||||||
|
void decodeCustomData(DxbcCodeSlice code);
|
||||||
|
void decodeOperation(DxbcCodeSlice code);
|
||||||
|
|
||||||
|
void decodeComponentSelection(DxbcRegister& reg, uint32_t token);
|
||||||
|
void decodeOperandExtensions(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token);
|
||||||
|
void decodeOperandImmediates(DxbcCodeSlice& code, DxbcRegister& reg);
|
||||||
|
void decodeOperandIndex(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token);
|
||||||
|
|
||||||
|
void decodeRegister(DxbcCodeSlice& code, DxbcRegister& reg, DxbcScalarType type);
|
||||||
|
void decodeImm32(DxbcCodeSlice& code, DxbcImmediate& imm, DxbcScalarType type);
|
||||||
|
|
||||||
|
void decodeOperand(DxbcCodeSlice& code, const DxbcInstOperandFormat& format);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
1255
src/dxbc/dxbc_defs.cpp
Normal file
1255
src/dxbc/dxbc_defs.cpp
Normal file
File diff suppressed because it is too large
Load diff
104
src/dxbc/dxbc_defs.h
Normal file
104
src/dxbc/dxbc_defs.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_enums.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
constexpr size_t DxbcMaxInterfaceRegs = 32;
|
||||||
|
constexpr size_t DxbcMaxOperandCount = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operand kind
|
||||||
|
*
|
||||||
|
* In the instruction format definition, this specified
|
||||||
|
* whether an operand uses an actual operand token, or
|
||||||
|
* whether it is stored as an immediate value.
|
||||||
|
*/
|
||||||
|
enum class DxbcOperandKind {
|
||||||
|
DstReg, ///< Destination register
|
||||||
|
SrcReg, ///< Source register
|
||||||
|
Imm32, ///< Constant number
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction class
|
||||||
|
*
|
||||||
|
* Instructions with a similar format are grouped into
|
||||||
|
* instruction classes in order to make implementing
|
||||||
|
* new instructions easier.
|
||||||
|
*/
|
||||||
|
enum class DxbcInstClass {
|
||||||
|
Declaration, ///< Interface or resource declaration
|
||||||
|
CustomData, ///< Immediate constant buffer
|
||||||
|
ControlFlow, ///< Control flow instructions
|
||||||
|
GeometryEmit, ///< Special geometry shader instructions
|
||||||
|
Atomic, ///< Atomic operations
|
||||||
|
AtomicCounter, ///< Atomic counter operations
|
||||||
|
Barrier, ///< Execution or memory barrier
|
||||||
|
BitExtract, ///< Bit field extract operations
|
||||||
|
BitInsert, ///< Bit field insert operations
|
||||||
|
BitScan, ///< Bit scan operations
|
||||||
|
BufferQuery, ///< Buffer query instruction
|
||||||
|
BufferLoad, ///< Structured or raw buffer load
|
||||||
|
BufferStore, ///< Structured or raw buffer store
|
||||||
|
ConvertFloat16, ///< 16-bit float packing/unpacking
|
||||||
|
ConvertFloat64, ///< 64-bit float conversion
|
||||||
|
HullShaderPhase, ///< Hull shader phase declaration
|
||||||
|
HullShaderInstCnt, ///< Hull shader phase instance count
|
||||||
|
Interpolate, ///< Input attribute interpolation
|
||||||
|
NoOperation, ///< The most useful instruction class
|
||||||
|
SparseCheckAccess, ///< Verifies sparse resource access
|
||||||
|
TextureQuery, ///< Texture query instruction
|
||||||
|
TextureQueryLod, ///< Texture LOD query instruction
|
||||||
|
TextureQueryMs, ///< Multisample texture query
|
||||||
|
TextureQueryMsPos, ///< Sample position query
|
||||||
|
TextureFetch, ///< Texture fetch instruction
|
||||||
|
TextureGather, ///< Texture gather instruction
|
||||||
|
TextureSample, ///< Texture sampling instruction
|
||||||
|
TypedUavLoad, ///< Typed UAV load
|
||||||
|
TypedUavStore, ///< Typed UAV store
|
||||||
|
VectorAlu, ///< Component-wise vector instructions
|
||||||
|
VectorCmov, ///< Component-wise conditional move
|
||||||
|
VectorCmp, ///< Component-wise vector comparison
|
||||||
|
VectorDeriv, ///< Vector derivatives
|
||||||
|
VectorDot, ///< Dot product instruction
|
||||||
|
VectorIdiv, ///< Component-wise integer division
|
||||||
|
VectorImul, ///< Component-wise integer multiplication
|
||||||
|
VectorMsad, ///< Component-wise sum of absolute difference
|
||||||
|
VectorShift, ///< Bit shift operations on vectors
|
||||||
|
VectorSinCos, ///< Sine and Cosine instruction
|
||||||
|
Undefined, ///< Instruction code not defined
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction operand format
|
||||||
|
*
|
||||||
|
* Stores the kind and the expected data type
|
||||||
|
* of an operand. Used when parsing instructions.
|
||||||
|
*/
|
||||||
|
struct DxbcInstOperandFormat {
|
||||||
|
DxbcOperandKind kind;
|
||||||
|
DxbcScalarType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction format
|
||||||
|
*
|
||||||
|
* Defines the instruction class as well as
|
||||||
|
* the format of the insttruction operands.
|
||||||
|
*/
|
||||||
|
struct DxbcInstFormat {
|
||||||
|
uint32_t operandCount = 0;
|
||||||
|
DxbcInstClass instructionClass = DxbcInstClass::Undefined;
|
||||||
|
DxbcInstOperandFormat operands[DxbcMaxOperandCount];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves instruction format info
|
||||||
|
*
|
||||||
|
* \param [in] opcode The opcode to retrieve
|
||||||
|
* \returns Instruction format info
|
||||||
|
*/
|
||||||
|
DxbcInstFormat dxbcInstructionFormat(DxbcOpcode opcode);
|
||||||
|
|
||||||
|
}
|
655
src/dxbc/dxbc_enums.h
Normal file
655
src/dxbc/dxbc_enums.h
Normal file
|
@ -0,0 +1,655 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_include.h"
|
||||||
|
#include "util_flags.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction code listing
|
||||||
|
*/
|
||||||
|
enum class DxbcOpcode : uint32_t {
|
||||||
|
Add = 0,
|
||||||
|
And = 1,
|
||||||
|
Break = 2,
|
||||||
|
Breakc = 3,
|
||||||
|
Call = 4,
|
||||||
|
Callc = 5,
|
||||||
|
Case = 6,
|
||||||
|
Continue = 7,
|
||||||
|
Continuec = 8,
|
||||||
|
Cut = 9,
|
||||||
|
Default = 10,
|
||||||
|
DerivRtx = 11,
|
||||||
|
DerivRty = 12,
|
||||||
|
Discard = 13,
|
||||||
|
Div = 14,
|
||||||
|
Dp2 = 15,
|
||||||
|
Dp3 = 16,
|
||||||
|
Dp4 = 17,
|
||||||
|
Else = 18,
|
||||||
|
Emit = 19,
|
||||||
|
EmitThenCut = 20,
|
||||||
|
EndIf = 21,
|
||||||
|
EndLoop = 22,
|
||||||
|
EndSwitch = 23,
|
||||||
|
Eq = 24,
|
||||||
|
Exp = 25,
|
||||||
|
Frc = 26,
|
||||||
|
FtoI = 27,
|
||||||
|
FtoU = 28,
|
||||||
|
Ge = 29,
|
||||||
|
IAdd = 30,
|
||||||
|
If = 31,
|
||||||
|
IEq = 32,
|
||||||
|
IGe = 33,
|
||||||
|
ILt = 34,
|
||||||
|
IMad = 35,
|
||||||
|
IMax = 36,
|
||||||
|
IMin = 37,
|
||||||
|
IMul = 38,
|
||||||
|
INe = 39,
|
||||||
|
INeg = 40,
|
||||||
|
IShl = 41,
|
||||||
|
IShr = 42,
|
||||||
|
ItoF = 43,
|
||||||
|
Label = 44,
|
||||||
|
Ld = 45,
|
||||||
|
LdMs = 46,
|
||||||
|
Log = 47,
|
||||||
|
Loop = 48,
|
||||||
|
Lt = 49,
|
||||||
|
Mad = 50,
|
||||||
|
Min = 51,
|
||||||
|
Max = 52,
|
||||||
|
CustomData = 53,
|
||||||
|
Mov = 54,
|
||||||
|
Movc = 55,
|
||||||
|
Mul = 56,
|
||||||
|
Ne = 57,
|
||||||
|
Nop = 58,
|
||||||
|
Not = 59,
|
||||||
|
Or = 60,
|
||||||
|
ResInfo = 61,
|
||||||
|
Ret = 62,
|
||||||
|
Retc = 63,
|
||||||
|
RoundNe = 64,
|
||||||
|
RoundNi = 65,
|
||||||
|
RoundPi = 66,
|
||||||
|
RoundZ = 67,
|
||||||
|
Rsq = 68,
|
||||||
|
Sample = 69,
|
||||||
|
SampleC = 70,
|
||||||
|
SampleClz = 71,
|
||||||
|
SampleL = 72,
|
||||||
|
SampleD = 73,
|
||||||
|
SampleB = 74,
|
||||||
|
Sqrt = 75,
|
||||||
|
Switch = 76,
|
||||||
|
SinCos = 77,
|
||||||
|
UDiv = 78,
|
||||||
|
ULt = 79,
|
||||||
|
UGe = 80,
|
||||||
|
UMul = 81,
|
||||||
|
UMad = 82,
|
||||||
|
UMax = 83,
|
||||||
|
UMin = 84,
|
||||||
|
UShr = 85,
|
||||||
|
UtoF = 86,
|
||||||
|
Xor = 87,
|
||||||
|
DclResource = 88,
|
||||||
|
DclConstantBuffer = 89,
|
||||||
|
DclSampler = 90,
|
||||||
|
DclIndexRange = 91,
|
||||||
|
DclGsOutputPrimitiveTopology = 92,
|
||||||
|
DclGsInputPrimitive = 93,
|
||||||
|
DclMaxOutputVertexCount = 94,
|
||||||
|
DclInput = 95,
|
||||||
|
DclInputSgv = 96,
|
||||||
|
DclInputSiv = 97,
|
||||||
|
DclInputPs = 98,
|
||||||
|
DclInputPsSgv = 99,
|
||||||
|
DclInputPsSiv = 100,
|
||||||
|
DclOutput = 101,
|
||||||
|
DclOutputSgv = 102,
|
||||||
|
DclOutputSiv = 103,
|
||||||
|
DclTemps = 104,
|
||||||
|
DclIndexableTemp = 105,
|
||||||
|
DclGlobalFlags = 106,
|
||||||
|
Reserved0 = 107,
|
||||||
|
Lod = 108,
|
||||||
|
Gather4 = 109,
|
||||||
|
SamplePos = 110,
|
||||||
|
SampleInfo = 111,
|
||||||
|
Reserved1 = 112,
|
||||||
|
HsDecls = 113,
|
||||||
|
HsControlPointPhase = 114,
|
||||||
|
HsForkPhase = 115,
|
||||||
|
HsJoinPhase = 116,
|
||||||
|
EmitStream = 117,
|
||||||
|
CutStream = 118,
|
||||||
|
EmitThenCutStream = 119,
|
||||||
|
InterfaceCall = 120,
|
||||||
|
BufInfo = 121,
|
||||||
|
DerivRtxCoarse = 122,
|
||||||
|
DerivRtxFine = 123,
|
||||||
|
DerivRtyCoarse = 124,
|
||||||
|
DerivRtyFine = 125,
|
||||||
|
Gather4C = 126,
|
||||||
|
Gather4Po = 127,
|
||||||
|
Gather4PoC = 128,
|
||||||
|
Rcp = 129,
|
||||||
|
F32toF16 = 130,
|
||||||
|
F16toF32 = 131,
|
||||||
|
UAddc = 132,
|
||||||
|
USubb = 133,
|
||||||
|
CountBits = 134,
|
||||||
|
FirstBitHi = 135,
|
||||||
|
FirstBitLo = 136,
|
||||||
|
FirstBitShi = 137,
|
||||||
|
UBfe = 138,
|
||||||
|
IBfe = 139,
|
||||||
|
Bfi = 140,
|
||||||
|
BfRev = 141,
|
||||||
|
Swapc = 142,
|
||||||
|
DclStream = 143,
|
||||||
|
DclFunctionBody = 144,
|
||||||
|
DclFunctionTable = 145,
|
||||||
|
DclInterface = 146,
|
||||||
|
DclInputControlPointCount = 147,
|
||||||
|
DclOutputControlPointCount = 148,
|
||||||
|
DclTessDomain = 149,
|
||||||
|
DclTessPartitioning = 150,
|
||||||
|
DclTessOutputPrimitive = 151,
|
||||||
|
DclHsMaxTessFactor = 152,
|
||||||
|
DclHsForkPhaseInstanceCount = 153,
|
||||||
|
DclHsJoinPhaseInstanceCount = 154,
|
||||||
|
DclThreadGroup = 155,
|
||||||
|
DclUavTyped = 156,
|
||||||
|
DclUavRaw = 157,
|
||||||
|
DclUavStructured = 158,
|
||||||
|
DclThreadGroupSharedMemoryRaw = 159,
|
||||||
|
DclThreadGroupSharedMemoryStructured = 160,
|
||||||
|
DclResourceRaw = 161,
|
||||||
|
DclResourceStructured = 162,
|
||||||
|
LdUavTyped = 163,
|
||||||
|
StoreUavTyped = 164,
|
||||||
|
LdRaw = 165,
|
||||||
|
StoreRaw = 166,
|
||||||
|
LdStructured = 167,
|
||||||
|
StoreStructured = 168,
|
||||||
|
AtomicAnd = 169,
|
||||||
|
AtomicOr = 170,
|
||||||
|
AtomicXor = 171,
|
||||||
|
AtomicCmpStore = 172,
|
||||||
|
AtomicIAdd = 173,
|
||||||
|
AtomicIMax = 174,
|
||||||
|
AtomicIMin = 175,
|
||||||
|
AtomicUMax = 176,
|
||||||
|
AtomicUMin = 177,
|
||||||
|
ImmAtomicAlloc = 178,
|
||||||
|
ImmAtomicConsume = 179,
|
||||||
|
ImmAtomicIAdd = 180,
|
||||||
|
ImmAtomicAnd = 181,
|
||||||
|
ImmAtomicOr = 182,
|
||||||
|
ImmAtomicXor = 183,
|
||||||
|
ImmAtomicExch = 184,
|
||||||
|
ImmAtomicCmpExch = 185,
|
||||||
|
ImmAtomicIMax = 186,
|
||||||
|
ImmAtomicIMin = 187,
|
||||||
|
ImmAtomicUMax = 188,
|
||||||
|
ImmAtomicUMin = 189,
|
||||||
|
Sync = 190,
|
||||||
|
DAdd = 191,
|
||||||
|
DMax = 192,
|
||||||
|
DMin = 193,
|
||||||
|
DMul = 194,
|
||||||
|
DEq = 195,
|
||||||
|
DGe = 196,
|
||||||
|
DLt = 197,
|
||||||
|
DNe = 198,
|
||||||
|
DMov = 199,
|
||||||
|
DMovc = 200,
|
||||||
|
DtoF = 201,
|
||||||
|
FtoD = 202,
|
||||||
|
EvalSnapped = 203,
|
||||||
|
EvalSampleIndex = 204,
|
||||||
|
EvalCentroid = 205,
|
||||||
|
DclGsInstanceCount = 206,
|
||||||
|
Abort = 207,
|
||||||
|
DebugBreak = 208,
|
||||||
|
ReservedBegin11_1 = 209,
|
||||||
|
DDiv = 210,
|
||||||
|
DFma = 211,
|
||||||
|
DRcp = 212,
|
||||||
|
Msad = 213,
|
||||||
|
DtoI = 214,
|
||||||
|
DtoU = 215,
|
||||||
|
ItoD = 216,
|
||||||
|
UtoD = 217,
|
||||||
|
ReservedBegin11_2 = 218,
|
||||||
|
Gather4S = 219,
|
||||||
|
Gather4CS = 220,
|
||||||
|
Gather4PoS = 221,
|
||||||
|
Gather4PoCS = 222,
|
||||||
|
LdS = 223,
|
||||||
|
LdMsS = 224,
|
||||||
|
LdUavTypedS = 225,
|
||||||
|
LdRawS = 226,
|
||||||
|
LdStructuredS = 227,
|
||||||
|
SampleLS = 228,
|
||||||
|
SampleClzS = 229,
|
||||||
|
SampleClampS = 230,
|
||||||
|
SampleBClampS = 231,
|
||||||
|
SampleDClampS = 232,
|
||||||
|
SampleCClampS = 233,
|
||||||
|
CheckAccessFullyMapped = 234,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Extended opcode
|
||||||
|
*/
|
||||||
|
enum class DxbcExtOpcode : uint32_t {
|
||||||
|
Empty = 0,
|
||||||
|
SampleControls = 1,
|
||||||
|
ResourceDim = 2,
|
||||||
|
ResourceReturnType = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operand type
|
||||||
|
*
|
||||||
|
* Selects the 'register file' from which
|
||||||
|
* to retrieve an operand's value.
|
||||||
|
*/
|
||||||
|
enum class DxbcOperandType : uint32_t {
|
||||||
|
Temp = 0,
|
||||||
|
Input = 1,
|
||||||
|
Output = 2,
|
||||||
|
IndexableTemp = 3,
|
||||||
|
Imm32 = 4,
|
||||||
|
Imm64 = 5,
|
||||||
|
Sampler = 6,
|
||||||
|
Resource = 7,
|
||||||
|
ConstantBuffer = 8,
|
||||||
|
ImmediateConstantBuffer = 9,
|
||||||
|
Label = 10,
|
||||||
|
InputPrimitiveId = 11,
|
||||||
|
OutputDepth = 12,
|
||||||
|
Null = 13,
|
||||||
|
Rasterizer = 14,
|
||||||
|
OutputCoverageMask = 15,
|
||||||
|
Stream = 16,
|
||||||
|
FunctionBody = 17,
|
||||||
|
FunctionTable = 18,
|
||||||
|
Interface = 19,
|
||||||
|
FunctionInput = 20,
|
||||||
|
FunctionOutput = 21,
|
||||||
|
OutputControlPointId = 22,
|
||||||
|
InputForkInstanceId = 23,
|
||||||
|
InputJoinInstanceId = 24,
|
||||||
|
InputControlPoint = 25,
|
||||||
|
OutputControlPoint = 26,
|
||||||
|
InputPatchConstant = 27,
|
||||||
|
InputDomainPoint = 28,
|
||||||
|
ThisPointer = 29,
|
||||||
|
UnorderedAccessView = 30,
|
||||||
|
ThreadGroupSharedMemory = 31,
|
||||||
|
InputThreadId = 32,
|
||||||
|
InputThreadGroupId = 33,
|
||||||
|
InputThreadIdInGroup = 34,
|
||||||
|
InputCoverageMask = 35,
|
||||||
|
InputThreadIndexInGroup = 36,
|
||||||
|
InputGsInstanceId = 37,
|
||||||
|
OutputDepthGe = 38,
|
||||||
|
OutputDepthLe = 39,
|
||||||
|
CycleCounter = 40,
|
||||||
|
OutputStencilRef = 41,
|
||||||
|
InputInnerCoverage = 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Number of components
|
||||||
|
*
|
||||||
|
* Used by operands to determine whether the
|
||||||
|
* operand has one, four or zero components.
|
||||||
|
*/
|
||||||
|
enum class DxbcComponentCount : uint32_t {
|
||||||
|
Component0 = 0,
|
||||||
|
Component1 = 1,
|
||||||
|
Component4 = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Component selection mode
|
||||||
|
*
|
||||||
|
* When an operand has four components, the
|
||||||
|
* component selection mode deterines which
|
||||||
|
* components are used for the operation.
|
||||||
|
*/
|
||||||
|
enum class DxbcRegMode : uint32_t {
|
||||||
|
Mask = 0,
|
||||||
|
Swizzle = 1,
|
||||||
|
Select1 = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Index representation
|
||||||
|
*
|
||||||
|
* Determines how an operand
|
||||||
|
* register index is stored.
|
||||||
|
*/
|
||||||
|
enum class DxbcOperandIndexRepresentation : uint32_t {
|
||||||
|
Imm32 = 0,
|
||||||
|
Imm64 = 1,
|
||||||
|
Relative = 2,
|
||||||
|
Imm32Relative = 3,
|
||||||
|
Imm64Relative = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Extended operand type
|
||||||
|
*/
|
||||||
|
enum class DxbcOperandExt : uint32_t {
|
||||||
|
OperandModifier = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resource dimension
|
||||||
|
* The type of a resource.
|
||||||
|
*/
|
||||||
|
enum class DxbcResourceDim : uint32_t {
|
||||||
|
Unknown = 0,
|
||||||
|
Buffer = 1,
|
||||||
|
Texture1D = 2,
|
||||||
|
Texture2D = 3,
|
||||||
|
Texture2DMs = 4,
|
||||||
|
Texture3D = 5,
|
||||||
|
TextureCube = 6,
|
||||||
|
Texture1DArr = 7,
|
||||||
|
Texture2DArr = 8,
|
||||||
|
Texture2DMsArr = 9,
|
||||||
|
TextureCubeArr = 10,
|
||||||
|
RawBuffer = 11,
|
||||||
|
StructuredBuffer = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resource return type
|
||||||
|
* Data type for resource read ops.
|
||||||
|
*/
|
||||||
|
enum class DxbcResourceReturnType : uint32_t {
|
||||||
|
Unorm = 1,
|
||||||
|
Snorm = 2,
|
||||||
|
Sint = 3,
|
||||||
|
Uint = 4,
|
||||||
|
Float = 5,
|
||||||
|
Mixed = 6, /// ?
|
||||||
|
Double = 7,
|
||||||
|
Continued = 8, /// ?
|
||||||
|
Unused = 9, /// ?
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Register component type
|
||||||
|
* Data type of a register component.
|
||||||
|
*/
|
||||||
|
enum class DxbcRegisterComponentType : uint32_t {
|
||||||
|
Unknown = 0,
|
||||||
|
Uint32 = 1,
|
||||||
|
Sint32 = 2,
|
||||||
|
Float32 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction return type
|
||||||
|
*/
|
||||||
|
enum class DxbcInstructionReturnType : uint32_t {
|
||||||
|
Float = 0,
|
||||||
|
Uint = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class DxbcSystemValue : uint32_t {
|
||||||
|
None = 0,
|
||||||
|
Position = 1,
|
||||||
|
ClipDistance = 2,
|
||||||
|
CullDistance = 3,
|
||||||
|
RenderTargetId = 4,
|
||||||
|
ViewportId = 5,
|
||||||
|
VertexId = 6,
|
||||||
|
PrimitiveId = 7,
|
||||||
|
InstanceId = 8,
|
||||||
|
IsFrontFace = 9,
|
||||||
|
SampleIndex = 10,
|
||||||
|
FinalQuadUeq0EdgeTessFactor = 11,
|
||||||
|
FinalQuadVeq0EdgeTessFactor = 12,
|
||||||
|
FinalQuadUeq1EdgeTessFactor = 13,
|
||||||
|
FinalQuadVeq1EdgeTessFactor = 14,
|
||||||
|
FinalQuadUInsideTessFactor = 15,
|
||||||
|
FinalQuadVInsideTessFactor = 16,
|
||||||
|
FinalTriUeq0EdgeTessFactor = 17,
|
||||||
|
FinalTriVeq0EdgeTessFactor = 18,
|
||||||
|
FinalTriWeq0EdgeTessFactor = 19,
|
||||||
|
FinalTriInsideTessFactor = 20,
|
||||||
|
FinalLineDetailTessFactor = 21,
|
||||||
|
FinalLineDensityTessFactor = 22,
|
||||||
|
Target = 64,
|
||||||
|
Depth = 65,
|
||||||
|
Coverage = 66,
|
||||||
|
DepthGe = 67,
|
||||||
|
DepthLe = 68
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class DxbcInterpolationMode : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
Constant = 1,
|
||||||
|
Linear = 2,
|
||||||
|
LinearCentroid = 3,
|
||||||
|
LinearNoPerspective = 4,
|
||||||
|
LinearNoPerspectiveCentroid = 5,
|
||||||
|
LinearSample = 6,
|
||||||
|
LinearNoPerspectiveSample = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class DxbcGlobalFlag : uint32_t {
|
||||||
|
RefactoringAllowed = 0,
|
||||||
|
DoublePrecision = 1,
|
||||||
|
EarlyFragmentTests = 2,
|
||||||
|
RawStructuredBuffers = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxbcGlobalFlags = Flags<DxbcGlobalFlag>;
|
||||||
|
|
||||||
|
enum class DxbcZeroTest : uint32_t {
|
||||||
|
TestZ = 0,
|
||||||
|
TestNz = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DxbcResinfoType : uint32_t {
|
||||||
|
Float = 0,
|
||||||
|
RcpFloat = 1,
|
||||||
|
Uint = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DxbcSyncFlag : uint32_t {
|
||||||
|
ThreadsInGroup = 0,
|
||||||
|
ThreadGroupSharedMemory = 1,
|
||||||
|
UavMemoryGroup = 2,
|
||||||
|
UavMemoryGlobal = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxbcSyncFlags = Flags<DxbcSyncFlag>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Geometry shader input primitive
|
||||||
|
*/
|
||||||
|
enum class DxbcPrimitive : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
Point = 1,
|
||||||
|
Line = 2,
|
||||||
|
Triangle = 3,
|
||||||
|
LineAdj = 6,
|
||||||
|
TriangleAdj = 7,
|
||||||
|
Patch1 = 8,
|
||||||
|
Patch2 = 9,
|
||||||
|
Patch3 = 10,
|
||||||
|
Patch4 = 11,
|
||||||
|
Patch5 = 12,
|
||||||
|
Patch6 = 13,
|
||||||
|
Patch7 = 14,
|
||||||
|
Patch8 = 15,
|
||||||
|
Patch9 = 16,
|
||||||
|
Patch10 = 17,
|
||||||
|
Patch11 = 18,
|
||||||
|
Patch12 = 19,
|
||||||
|
Patch13 = 20,
|
||||||
|
Patch14 = 21,
|
||||||
|
Patch15 = 22,
|
||||||
|
Patch16 = 23,
|
||||||
|
Patch17 = 24,
|
||||||
|
Patch18 = 25,
|
||||||
|
Patch19 = 26,
|
||||||
|
Patch20 = 27,
|
||||||
|
Patch21 = 28,
|
||||||
|
Patch22 = 29,
|
||||||
|
Patch23 = 30,
|
||||||
|
Patch24 = 31,
|
||||||
|
Patch25 = 32,
|
||||||
|
Patch26 = 33,
|
||||||
|
Patch27 = 34,
|
||||||
|
Patch28 = 35,
|
||||||
|
Patch29 = 36,
|
||||||
|
Patch30 = 37,
|
||||||
|
Patch31 = 38,
|
||||||
|
Patch32 = 39,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Geometry shader output topology
|
||||||
|
*/
|
||||||
|
enum class DxbcPrimitiveTopology : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
PointList = 1,
|
||||||
|
LineList = 2,
|
||||||
|
LineStrip = 3,
|
||||||
|
TriangleList = 4,
|
||||||
|
TriangleStrip = 5,
|
||||||
|
LineListAdj = 10,
|
||||||
|
LineStripAdj = 11,
|
||||||
|
TriangleListAdj = 12,
|
||||||
|
TriangleStripAdj = 13,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sampler operation mode
|
||||||
|
*/
|
||||||
|
enum class DxbcSamplerMode : uint32_t {
|
||||||
|
Default = 0,
|
||||||
|
Comparison = 1,
|
||||||
|
Mono = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Scalar value type
|
||||||
|
*
|
||||||
|
* Enumerates possible register component
|
||||||
|
* types. Scalar types are represented as
|
||||||
|
* a one-component vector type.
|
||||||
|
*/
|
||||||
|
enum class DxbcScalarType : uint32_t {
|
||||||
|
Uint32 = 0,
|
||||||
|
Uint64 = 1,
|
||||||
|
Sint32 = 2,
|
||||||
|
Sint64 = 3,
|
||||||
|
Float32 = 4,
|
||||||
|
Float64 = 5,
|
||||||
|
Bool = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tessellator domain
|
||||||
|
*/
|
||||||
|
enum class DxbcTessDomain : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
Isolines = 1,
|
||||||
|
Triangles = 2,
|
||||||
|
Quads = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tessellator partitioning
|
||||||
|
*/
|
||||||
|
enum class DxbcTessPartitioning : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
Integer = 1,
|
||||||
|
Pow2 = 2,
|
||||||
|
FractOdd = 3,
|
||||||
|
FractEven = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief UAV definition flags
|
||||||
|
*/
|
||||||
|
enum class DxbcUavFlag : uint32_t {
|
||||||
|
GloballyCoherent = 0,
|
||||||
|
RasterizerOrdered = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxbcUavFlags = Flags<DxbcUavFlag>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tessellator output primitive
|
||||||
|
*/
|
||||||
|
enum class DxbcTessOutputPrimitive : uint32_t {
|
||||||
|
Undefined = 0,
|
||||||
|
Point = 1,
|
||||||
|
Line = 2,
|
||||||
|
TriangleCw = 3,
|
||||||
|
TriangleCcw = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Custom data class
|
||||||
|
*
|
||||||
|
* Stores which type of custom data is
|
||||||
|
* referenced by the instruction.
|
||||||
|
*/
|
||||||
|
enum class DxbcCustomDataClass : uint32_t {
|
||||||
|
Comment = 0,
|
||||||
|
DebugInfo = 1,
|
||||||
|
Opaque = 2,
|
||||||
|
ImmConstBuf = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class DxbcResourceType : uint32_t {
|
||||||
|
Typed = 0,
|
||||||
|
Raw = 1,
|
||||||
|
Structured = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class DxbcConstantBufferAccessType : uint32_t {
|
||||||
|
StaticallyIndexed = 0,
|
||||||
|
DynamicallyIndexed = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
30
src/dxbc/dxbc_header.cpp
Normal file
30
src/dxbc/dxbc_header.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "dxbc_header.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcHeader::DxbcHeader(DxbcReader& reader) {
|
||||||
|
// FourCC at the start of the file, must be 'DXBC'
|
||||||
|
DxbcTag fourcc = reader.readTag();
|
||||||
|
|
||||||
|
if (fourcc != "DXBC")
|
||||||
|
throw DxvkError("DxbcHeader::DxbcHeader: Invalid fourcc, expected 'DXBC'");
|
||||||
|
|
||||||
|
// Stuff we don't actually need to store
|
||||||
|
reader.skip(4 * sizeof(uint32_t)); // Check sum
|
||||||
|
reader.skip(1 * sizeof(uint32_t)); // Constant 1
|
||||||
|
reader.skip(1 * sizeof(uint32_t)); // Bytecode length
|
||||||
|
|
||||||
|
// Number of chunks in the file
|
||||||
|
uint32_t chunkCount = reader.readu32();
|
||||||
|
|
||||||
|
// Chunk offsets are stored immediately after
|
||||||
|
for (uint32_t i = 0; i < chunkCount; i++)
|
||||||
|
m_chunkOffsets.push_back(reader.readu32());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcHeader::~DxbcHeader() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/dxbc/dxbc_header.h
Normal file
48
src/dxbc/dxbc_header.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dxbc_reader.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC header
|
||||||
|
*
|
||||||
|
* Stores information about the shader file itself
|
||||||
|
* and the data chunks stored inside the file.
|
||||||
|
*/
|
||||||
|
class DxbcHeader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcHeader(DxbcReader& reader);
|
||||||
|
~DxbcHeader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Number of chunks
|
||||||
|
* \returns Chunk count
|
||||||
|
*/
|
||||||
|
uint32_t numChunks() const {
|
||||||
|
return m_chunkOffsets.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Chunk offset
|
||||||
|
*
|
||||||
|
* Retrieves the offset of a chunk, in
|
||||||
|
* bytes, from the start of the file.
|
||||||
|
* \param [in] chunkId Chunk index
|
||||||
|
* \returns Byte offset of that chunk
|
||||||
|
*/
|
||||||
|
uint32_t chunkOffset(uint32_t chunkId) const {
|
||||||
|
return m_chunkOffsets.at(chunkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_chunkOffsets;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
18
src/dxbc/dxbc_include.h
Normal file
18
src/dxbc/dxbc_include.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include "util/com/com_guid.h"
|
||||||
|
#include "util/com/com_object.h"
|
||||||
|
#include "util/com/com_pointer.h"
|
||||||
|
|
||||||
|
#include "util/log/log.h"
|
||||||
|
#include "util/log/log_debug.h"
|
||||||
|
|
||||||
|
#include "util/rc/util_rc.h"
|
||||||
|
#include "util/rc/util_rc_ptr.h"
|
||||||
|
|
||||||
|
#include "util/util_bit.h"
|
||||||
|
#include "util/util_enum.h"
|
||||||
|
#include "util/util_error.h"
|
||||||
|
#include "util/util_string.h"
|
59
src/dxbc/dxbc_modinfo.h
Normal file
59
src/dxbc/dxbc_modinfo.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_options.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tessellation info
|
||||||
|
*
|
||||||
|
* Stores the maximum tessellation factor
|
||||||
|
* to export from tessellation shaders.
|
||||||
|
*/
|
||||||
|
struct DxbcTessInfo {
|
||||||
|
float maxTessFactor;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Xfb capture entry
|
||||||
|
*
|
||||||
|
* Stores an output variable to capture,
|
||||||
|
* as well as the buffer to write it to.
|
||||||
|
*/
|
||||||
|
struct DxbcXfbEntry {
|
||||||
|
const char* semanticName;
|
||||||
|
uint32_t semanticIndex;
|
||||||
|
uint32_t componentIndex;
|
||||||
|
uint32_t componentCount;
|
||||||
|
uint32_t streamId;
|
||||||
|
uint32_t bufferId;
|
||||||
|
uint32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Xfb info
|
||||||
|
*
|
||||||
|
* Stores capture entries and output buffer
|
||||||
|
* strides. This structure must only be
|
||||||
|
* defined if \c entryCount is non-zero.
|
||||||
|
*/
|
||||||
|
struct DxbcXfbInfo {
|
||||||
|
uint32_t entryCount;
|
||||||
|
DxbcXfbEntry entries[128];
|
||||||
|
uint32_t strides[4];
|
||||||
|
int32_t rasterizedStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader module info
|
||||||
|
*
|
||||||
|
* Stores information which may affect shader compilation.
|
||||||
|
* This data can be supplied by the client API implementation.
|
||||||
|
*/
|
||||||
|
struct DxbcModuleInfo {
|
||||||
|
DxbcOptions options;
|
||||||
|
DxbcTessInfo* tess = nullptr;
|
||||||
|
DxbcXfbInfo* xfb = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
116
src/dxbc/dxbc_module.cpp
Normal file
116
src/dxbc/dxbc_module.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#include "dxbc_analysis.h"
|
||||||
|
#include "dxbc_compiler.h"
|
||||||
|
#include "dxbc_module.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcModule::DxbcModule(DxbcReader& reader)
|
||||||
|
: m_header(reader) {
|
||||||
|
for (uint32_t i = 0; i < m_header.numChunks(); i++) {
|
||||||
|
|
||||||
|
// The chunk tag is stored at the beginning of each chunk
|
||||||
|
auto chunkReader = reader.clone(m_header.chunkOffset(i));
|
||||||
|
auto tag = chunkReader.readTag();
|
||||||
|
|
||||||
|
// The chunk size follows right after the four-character
|
||||||
|
// code. This does not include the eight bytes that are
|
||||||
|
// consumed by the FourCC and chunk length entry.
|
||||||
|
auto chunkLength = chunkReader.readu32();
|
||||||
|
|
||||||
|
chunkReader = chunkReader.clone(8);
|
||||||
|
chunkReader = chunkReader.resize(chunkLength);
|
||||||
|
|
||||||
|
if ((tag == "SHDR") || (tag == "SHEX"))
|
||||||
|
m_shexChunk = new DxbcShex(chunkReader);
|
||||||
|
|
||||||
|
if ((tag == "ISGN") || (tag == "ISG1"))
|
||||||
|
m_isgnChunk = new DxbcIsgn(chunkReader, tag);
|
||||||
|
|
||||||
|
if ((tag == "OSGN") || (tag == "OSG5") || (tag == "OSG1"))
|
||||||
|
m_osgnChunk = new DxbcIsgn(chunkReader, tag);
|
||||||
|
|
||||||
|
if ((tag == "PCSG") || (tag == "PSG1"))
|
||||||
|
m_psgnChunk = new DxbcIsgn(chunkReader, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcModule::~DxbcModule() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcCompiler::ShaderCreateInfo DxbcModule::compile(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const std::string& fileName) const {
|
||||||
|
if (m_shexChunk == nullptr)
|
||||||
|
throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk");
|
||||||
|
|
||||||
|
DxbcAnalysisInfo analysisInfo;
|
||||||
|
|
||||||
|
DxbcAnalyzer analyzer(moduleInfo,
|
||||||
|
m_shexChunk->programInfo(),
|
||||||
|
m_isgnChunk, m_osgnChunk,
|
||||||
|
m_psgnChunk, analysisInfo);
|
||||||
|
|
||||||
|
this->runAnalyzer(analyzer, m_shexChunk->slice());
|
||||||
|
|
||||||
|
DxbcCompiler compiler(
|
||||||
|
fileName, moduleInfo,
|
||||||
|
m_shexChunk->programInfo(),
|
||||||
|
m_isgnChunk, m_osgnChunk,
|
||||||
|
m_psgnChunk, analysisInfo);
|
||||||
|
|
||||||
|
this->runCompiler(compiler, m_shexChunk->slice());
|
||||||
|
|
||||||
|
return compiler.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcCompiler::ShaderCreateInfo DxbcModule::compilePassthroughShader(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const std::string& fileName) const {
|
||||||
|
if (m_shexChunk == nullptr)
|
||||||
|
throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk");
|
||||||
|
|
||||||
|
DxbcAnalysisInfo analysisInfo;
|
||||||
|
|
||||||
|
DxbcCompiler compiler(
|
||||||
|
fileName, moduleInfo,
|
||||||
|
DxbcProgramType::GeometryShader,
|
||||||
|
m_osgnChunk, m_osgnChunk,
|
||||||
|
m_psgnChunk, analysisInfo);
|
||||||
|
|
||||||
|
compiler.processXfbPassthrough();
|
||||||
|
return compiler.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcModule::runAnalyzer(
|
||||||
|
DxbcAnalyzer& analyzer,
|
||||||
|
DxbcCodeSlice slice) const {
|
||||||
|
DxbcDecodeContext decoder;
|
||||||
|
|
||||||
|
while (!slice.atEnd()) {
|
||||||
|
decoder.decodeInstruction(slice);
|
||||||
|
|
||||||
|
analyzer.processInstruction(
|
||||||
|
decoder.getInstruction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcModule::runCompiler(
|
||||||
|
DxbcCompiler& compiler,
|
||||||
|
DxbcCodeSlice slice) const {
|
||||||
|
DxbcDecodeContext decoder;
|
||||||
|
|
||||||
|
while (!slice.atEnd()) {
|
||||||
|
decoder.decodeInstruction(slice);
|
||||||
|
|
||||||
|
compiler.processInstruction(
|
||||||
|
decoder.getInstruction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
src/dxbc/dxbc_module.h
Normal file
102
src/dxbc/dxbc_module.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//#include "../dxvk/dxvk_shader.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "dxbc_chunk_isgn.h"
|
||||||
|
#include "dxbc_chunk_shex.h"
|
||||||
|
#include "dxbc_header.h"
|
||||||
|
#include "dxbc_modinfo.h"
|
||||||
|
#include "dxbc_reader.h"
|
||||||
|
#include "dxbc_compiler.h"
|
||||||
|
|
||||||
|
// References used for figuring out DXBC:
|
||||||
|
// - https://github.com/tgjones/slimshader-cpp
|
||||||
|
// - Wine
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
class DxbcAnalyzer;
|
||||||
|
class DxbcCompiler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC shader module
|
||||||
|
*
|
||||||
|
* Reads the DXBC byte code and extracts information
|
||||||
|
* about the resource bindings and the instruction
|
||||||
|
* stream. A module can then be compiled to SPIR-V.
|
||||||
|
*/
|
||||||
|
class DxbcModule {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcModule(DxbcReader& reader);
|
||||||
|
~DxbcModule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader type
|
||||||
|
* \returns Shader type
|
||||||
|
*/
|
||||||
|
std::optional<DxbcProgramInfo> programInfo() const {
|
||||||
|
if (m_shexChunk == nullptr)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return m_shexChunk->programInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Input and output signature chunks
|
||||||
|
*
|
||||||
|
* Parts of the D3D11 API need access to the
|
||||||
|
* input or output signature of the shader.
|
||||||
|
*/
|
||||||
|
Rc<DxbcIsgn> isgn() const { return m_isgnChunk; }
|
||||||
|
Rc<DxbcIsgn> osgn() const { return m_osgnChunk; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compiles DXBC shader to SPIR-V module
|
||||||
|
*
|
||||||
|
* \param [in] moduleInfo DXBC module info
|
||||||
|
* \param [in] fileName File name, will be added to
|
||||||
|
* the compiled SPIR-V for debugging purposes.
|
||||||
|
* \returns The compiled shader object
|
||||||
|
*/
|
||||||
|
DxbcCompiler::ShaderCreateInfo compile(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const std::string& fileName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compiles a pass-through geometry shader
|
||||||
|
*
|
||||||
|
* Applications can pass a vertex shader to create
|
||||||
|
* a geometry shader with stream output. In this
|
||||||
|
* case, we have to create a passthrough geometry
|
||||||
|
* shader, which operates in point to point mode.
|
||||||
|
* \param [in] moduleInfo DXBC module info
|
||||||
|
* \param [in] fileName SPIR-V shader name
|
||||||
|
*/
|
||||||
|
DxbcCompiler::ShaderCreateInfo compilePassthroughShader(
|
||||||
|
const DxbcModuleInfo& moduleInfo,
|
||||||
|
const std::string& fileName) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcHeader m_header;
|
||||||
|
|
||||||
|
Rc<DxbcIsgn> m_isgnChunk;
|
||||||
|
Rc<DxbcIsgn> m_osgnChunk;
|
||||||
|
Rc<DxbcIsgn> m_psgnChunk;
|
||||||
|
Rc<DxbcShex> m_shexChunk;
|
||||||
|
|
||||||
|
void runAnalyzer(
|
||||||
|
DxbcAnalyzer& analyzer,
|
||||||
|
DxbcCodeSlice slice) const;
|
||||||
|
|
||||||
|
void runCompiler(
|
||||||
|
DxbcCompiler& compiler,
|
||||||
|
DxbcCodeSlice slice) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
445
src/dxbc/dxbc_names.cpp
Normal file
445
src/dxbc/dxbc_names.cpp
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
#include "dxbc_names.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOpcode e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcOpcode::Add);
|
||||||
|
ENUM_NAME(DxbcOpcode::And);
|
||||||
|
ENUM_NAME(DxbcOpcode::Break);
|
||||||
|
ENUM_NAME(DxbcOpcode::Breakc);
|
||||||
|
ENUM_NAME(DxbcOpcode::Call);
|
||||||
|
ENUM_NAME(DxbcOpcode::Callc);
|
||||||
|
ENUM_NAME(DxbcOpcode::Case);
|
||||||
|
ENUM_NAME(DxbcOpcode::Continue);
|
||||||
|
ENUM_NAME(DxbcOpcode::Continuec);
|
||||||
|
ENUM_NAME(DxbcOpcode::Cut);
|
||||||
|
ENUM_NAME(DxbcOpcode::Default);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRtx);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRty);
|
||||||
|
ENUM_NAME(DxbcOpcode::Discard);
|
||||||
|
ENUM_NAME(DxbcOpcode::Div);
|
||||||
|
ENUM_NAME(DxbcOpcode::Dp2);
|
||||||
|
ENUM_NAME(DxbcOpcode::Dp3);
|
||||||
|
ENUM_NAME(DxbcOpcode::Dp4);
|
||||||
|
ENUM_NAME(DxbcOpcode::Else);
|
||||||
|
ENUM_NAME(DxbcOpcode::Emit);
|
||||||
|
ENUM_NAME(DxbcOpcode::EmitThenCut);
|
||||||
|
ENUM_NAME(DxbcOpcode::EndIf);
|
||||||
|
ENUM_NAME(DxbcOpcode::EndLoop);
|
||||||
|
ENUM_NAME(DxbcOpcode::EndSwitch);
|
||||||
|
ENUM_NAME(DxbcOpcode::Eq);
|
||||||
|
ENUM_NAME(DxbcOpcode::Exp);
|
||||||
|
ENUM_NAME(DxbcOpcode::Frc);
|
||||||
|
ENUM_NAME(DxbcOpcode::FtoI);
|
||||||
|
ENUM_NAME(DxbcOpcode::FtoU);
|
||||||
|
ENUM_NAME(DxbcOpcode::Ge);
|
||||||
|
ENUM_NAME(DxbcOpcode::IAdd);
|
||||||
|
ENUM_NAME(DxbcOpcode::If);
|
||||||
|
ENUM_NAME(DxbcOpcode::IEq);
|
||||||
|
ENUM_NAME(DxbcOpcode::IGe);
|
||||||
|
ENUM_NAME(DxbcOpcode::ILt);
|
||||||
|
ENUM_NAME(DxbcOpcode::IMad);
|
||||||
|
ENUM_NAME(DxbcOpcode::IMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::IMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::IMul);
|
||||||
|
ENUM_NAME(DxbcOpcode::INe);
|
||||||
|
ENUM_NAME(DxbcOpcode::INeg);
|
||||||
|
ENUM_NAME(DxbcOpcode::IShl);
|
||||||
|
ENUM_NAME(DxbcOpcode::IShr);
|
||||||
|
ENUM_NAME(DxbcOpcode::ItoF);
|
||||||
|
ENUM_NAME(DxbcOpcode::Label);
|
||||||
|
ENUM_NAME(DxbcOpcode::Ld);
|
||||||
|
ENUM_NAME(DxbcOpcode::LdMs);
|
||||||
|
ENUM_NAME(DxbcOpcode::Log);
|
||||||
|
ENUM_NAME(DxbcOpcode::Loop);
|
||||||
|
ENUM_NAME(DxbcOpcode::Lt);
|
||||||
|
ENUM_NAME(DxbcOpcode::Mad);
|
||||||
|
ENUM_NAME(DxbcOpcode::Min);
|
||||||
|
ENUM_NAME(DxbcOpcode::Max);
|
||||||
|
ENUM_NAME(DxbcOpcode::CustomData);
|
||||||
|
ENUM_NAME(DxbcOpcode::Mov);
|
||||||
|
ENUM_NAME(DxbcOpcode::Movc);
|
||||||
|
ENUM_NAME(DxbcOpcode::Mul);
|
||||||
|
ENUM_NAME(DxbcOpcode::Ne);
|
||||||
|
ENUM_NAME(DxbcOpcode::Nop);
|
||||||
|
ENUM_NAME(DxbcOpcode::Not);
|
||||||
|
ENUM_NAME(DxbcOpcode::Or);
|
||||||
|
ENUM_NAME(DxbcOpcode::ResInfo);
|
||||||
|
ENUM_NAME(DxbcOpcode::Ret);
|
||||||
|
ENUM_NAME(DxbcOpcode::Retc);
|
||||||
|
ENUM_NAME(DxbcOpcode::RoundNe);
|
||||||
|
ENUM_NAME(DxbcOpcode::RoundNi);
|
||||||
|
ENUM_NAME(DxbcOpcode::RoundPi);
|
||||||
|
ENUM_NAME(DxbcOpcode::RoundZ);
|
||||||
|
ENUM_NAME(DxbcOpcode::Rsq);
|
||||||
|
ENUM_NAME(DxbcOpcode::Sample);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleC);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleClz);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleL);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleD);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleB);
|
||||||
|
ENUM_NAME(DxbcOpcode::Sqrt);
|
||||||
|
ENUM_NAME(DxbcOpcode::Switch);
|
||||||
|
ENUM_NAME(DxbcOpcode::SinCos);
|
||||||
|
ENUM_NAME(DxbcOpcode::UDiv);
|
||||||
|
ENUM_NAME(DxbcOpcode::ULt);
|
||||||
|
ENUM_NAME(DxbcOpcode::UGe);
|
||||||
|
ENUM_NAME(DxbcOpcode::UMul);
|
||||||
|
ENUM_NAME(DxbcOpcode::UMad);
|
||||||
|
ENUM_NAME(DxbcOpcode::UMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::UMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::UShr);
|
||||||
|
ENUM_NAME(DxbcOpcode::UtoF);
|
||||||
|
ENUM_NAME(DxbcOpcode::Xor);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclResource);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclConstantBuffer);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclSampler);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclIndexRange);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclGsOutputPrimitiveTopology);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclGsInputPrimitive);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclMaxOutputVertexCount);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInput);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputSgv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputSiv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputPs);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputPsSgv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputPsSiv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclOutput);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclOutputSgv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclOutputSiv);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclTemps);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclIndexableTemp);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclGlobalFlags);
|
||||||
|
ENUM_NAME(DxbcOpcode::Reserved0);
|
||||||
|
ENUM_NAME(DxbcOpcode::Lod);
|
||||||
|
ENUM_NAME(DxbcOpcode::Gather4);
|
||||||
|
ENUM_NAME(DxbcOpcode::SamplePos);
|
||||||
|
ENUM_NAME(DxbcOpcode::SampleInfo);
|
||||||
|
ENUM_NAME(DxbcOpcode::Reserved1);
|
||||||
|
ENUM_NAME(DxbcOpcode::HsDecls);
|
||||||
|
ENUM_NAME(DxbcOpcode::HsControlPointPhase);
|
||||||
|
ENUM_NAME(DxbcOpcode::HsForkPhase);
|
||||||
|
ENUM_NAME(DxbcOpcode::HsJoinPhase);
|
||||||
|
ENUM_NAME(DxbcOpcode::EmitStream);
|
||||||
|
ENUM_NAME(DxbcOpcode::CutStream);
|
||||||
|
ENUM_NAME(DxbcOpcode::EmitThenCutStream);
|
||||||
|
ENUM_NAME(DxbcOpcode::InterfaceCall);
|
||||||
|
ENUM_NAME(DxbcOpcode::BufInfo);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRtxCoarse);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRtxFine);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRtyCoarse);
|
||||||
|
ENUM_NAME(DxbcOpcode::DerivRtyFine);
|
||||||
|
ENUM_NAME(DxbcOpcode::Gather4C);
|
||||||
|
ENUM_NAME(DxbcOpcode::Gather4Po);
|
||||||
|
ENUM_NAME(DxbcOpcode::Gather4PoC);
|
||||||
|
ENUM_NAME(DxbcOpcode::Rcp);
|
||||||
|
ENUM_NAME(DxbcOpcode::F32toF16);
|
||||||
|
ENUM_NAME(DxbcOpcode::F16toF32);
|
||||||
|
ENUM_NAME(DxbcOpcode::UAddc);
|
||||||
|
ENUM_NAME(DxbcOpcode::USubb);
|
||||||
|
ENUM_NAME(DxbcOpcode::CountBits);
|
||||||
|
ENUM_NAME(DxbcOpcode::FirstBitHi);
|
||||||
|
ENUM_NAME(DxbcOpcode::FirstBitLo);
|
||||||
|
ENUM_NAME(DxbcOpcode::FirstBitShi);
|
||||||
|
ENUM_NAME(DxbcOpcode::UBfe);
|
||||||
|
ENUM_NAME(DxbcOpcode::IBfe);
|
||||||
|
ENUM_NAME(DxbcOpcode::Bfi);
|
||||||
|
ENUM_NAME(DxbcOpcode::BfRev);
|
||||||
|
ENUM_NAME(DxbcOpcode::Swapc);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclStream);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclFunctionBody);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclFunctionTable);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInterface);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclInputControlPointCount);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclOutputControlPointCount);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclTessDomain);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclTessPartitioning);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclTessOutputPrimitive);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclHsMaxTessFactor);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclHsForkPhaseInstanceCount);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclHsJoinPhaseInstanceCount);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclThreadGroup);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclUavTyped);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclUavRaw);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclUavStructured);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclThreadGroupSharedMemoryRaw);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclThreadGroupSharedMemoryStructured);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclResourceRaw);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclResourceStructured);
|
||||||
|
ENUM_NAME(DxbcOpcode::LdUavTyped);
|
||||||
|
ENUM_NAME(DxbcOpcode::StoreUavTyped);
|
||||||
|
ENUM_NAME(DxbcOpcode::LdRaw);
|
||||||
|
ENUM_NAME(DxbcOpcode::StoreRaw);
|
||||||
|
ENUM_NAME(DxbcOpcode::LdStructured);
|
||||||
|
ENUM_NAME(DxbcOpcode::StoreStructured);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicAnd);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicOr);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicXor);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicCmpStore);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicIAdd);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicIMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicIMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicUMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::AtomicUMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicAlloc);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicConsume);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicIAdd);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicAnd);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicOr);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicXor);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicExch);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicCmpExch);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicIMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicIMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicUMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::ImmAtomicUMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::Sync);
|
||||||
|
ENUM_NAME(DxbcOpcode::DAdd);
|
||||||
|
ENUM_NAME(DxbcOpcode::DMax);
|
||||||
|
ENUM_NAME(DxbcOpcode::DMin);
|
||||||
|
ENUM_NAME(DxbcOpcode::DMul);
|
||||||
|
ENUM_NAME(DxbcOpcode::DEq);
|
||||||
|
ENUM_NAME(DxbcOpcode::DGe);
|
||||||
|
ENUM_NAME(DxbcOpcode::DLt);
|
||||||
|
ENUM_NAME(DxbcOpcode::DNe);
|
||||||
|
ENUM_NAME(DxbcOpcode::DMov);
|
||||||
|
ENUM_NAME(DxbcOpcode::DMovc);
|
||||||
|
ENUM_NAME(DxbcOpcode::DtoF);
|
||||||
|
ENUM_NAME(DxbcOpcode::FtoD);
|
||||||
|
ENUM_NAME(DxbcOpcode::EvalSnapped);
|
||||||
|
ENUM_NAME(DxbcOpcode::EvalSampleIndex);
|
||||||
|
ENUM_NAME(DxbcOpcode::EvalCentroid);
|
||||||
|
ENUM_NAME(DxbcOpcode::DclGsInstanceCount);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcExtOpcode e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcExtOpcode::Empty);
|
||||||
|
ENUM_NAME(DxbcExtOpcode::SampleControls);
|
||||||
|
ENUM_NAME(DxbcExtOpcode::ResourceDim);
|
||||||
|
ENUM_NAME(DxbcExtOpcode::ResourceReturnType);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOperandType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcOperandType::Temp);
|
||||||
|
ENUM_NAME(DxbcOperandType::Input);
|
||||||
|
ENUM_NAME(DxbcOperandType::Output);
|
||||||
|
ENUM_NAME(DxbcOperandType::IndexableTemp);
|
||||||
|
ENUM_NAME(DxbcOperandType::Imm32);
|
||||||
|
ENUM_NAME(DxbcOperandType::Imm64);
|
||||||
|
ENUM_NAME(DxbcOperandType::Sampler);
|
||||||
|
ENUM_NAME(DxbcOperandType::Resource);
|
||||||
|
ENUM_NAME(DxbcOperandType::ConstantBuffer);
|
||||||
|
ENUM_NAME(DxbcOperandType::ImmediateConstantBuffer);
|
||||||
|
ENUM_NAME(DxbcOperandType::Label);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputPrimitiveId);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputDepth);
|
||||||
|
ENUM_NAME(DxbcOperandType::Null);
|
||||||
|
ENUM_NAME(DxbcOperandType::Rasterizer);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputCoverageMask);
|
||||||
|
ENUM_NAME(DxbcOperandType::Stream);
|
||||||
|
ENUM_NAME(DxbcOperandType::FunctionBody);
|
||||||
|
ENUM_NAME(DxbcOperandType::FunctionTable);
|
||||||
|
ENUM_NAME(DxbcOperandType::Interface);
|
||||||
|
ENUM_NAME(DxbcOperandType::FunctionInput);
|
||||||
|
ENUM_NAME(DxbcOperandType::FunctionOutput);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputControlPointId);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputForkInstanceId);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputJoinInstanceId);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputControlPoint);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputControlPoint);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputPatchConstant);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputDomainPoint);
|
||||||
|
ENUM_NAME(DxbcOperandType::ThisPointer);
|
||||||
|
ENUM_NAME(DxbcOperandType::UnorderedAccessView);
|
||||||
|
ENUM_NAME(DxbcOperandType::ThreadGroupSharedMemory);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputThreadId);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputThreadGroupId);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputThreadIdInGroup);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputCoverageMask);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputThreadIndexInGroup);
|
||||||
|
ENUM_NAME(DxbcOperandType::InputGsInstanceId);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputDepthGe);
|
||||||
|
ENUM_NAME(DxbcOperandType::OutputDepthLe);
|
||||||
|
ENUM_NAME(DxbcOperandType::CycleCounter);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, dxvk::DxbcOperandExt e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcOperandExt::OperandModifier);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcComponentCount e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcComponentCount::Component0);
|
||||||
|
ENUM_NAME(DxbcComponentCount::Component1);
|
||||||
|
ENUM_NAME(DxbcComponentCount::Component4);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcRegMode e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcRegMode::Mask);
|
||||||
|
ENUM_NAME(DxbcRegMode::Swizzle);
|
||||||
|
ENUM_NAME(DxbcRegMode::Select1);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOperandIndexRepresentation e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcOperandIndexRepresentation::Imm32);
|
||||||
|
ENUM_NAME(DxbcOperandIndexRepresentation::Imm64);
|
||||||
|
ENUM_NAME(DxbcOperandIndexRepresentation::Relative);
|
||||||
|
ENUM_NAME(DxbcOperandIndexRepresentation::Imm32Relative);
|
||||||
|
ENUM_NAME(DxbcOperandIndexRepresentation::Imm64Relative);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcResourceDim e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcResourceDim::Unknown);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Buffer);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture1D);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture2D);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture2DMs);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture3D);
|
||||||
|
ENUM_NAME(DxbcResourceDim::TextureCube);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture1DArr);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture2DArr);
|
||||||
|
ENUM_NAME(DxbcResourceDim::Texture2DMsArr);
|
||||||
|
ENUM_NAME(DxbcResourceDim::TextureCubeArr);
|
||||||
|
ENUM_NAME(DxbcResourceDim::RawBuffer);
|
||||||
|
ENUM_NAME(DxbcResourceDim::StructuredBuffer);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcResourceReturnType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Unorm);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Snorm);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Sint);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Uint);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Float);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Mixed);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Double);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Continued);
|
||||||
|
ENUM_NAME(DxbcResourceReturnType::Unused);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcRegisterComponentType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcRegisterComponentType::Unknown);
|
||||||
|
ENUM_NAME(DxbcRegisterComponentType::Uint32);
|
||||||
|
ENUM_NAME(DxbcRegisterComponentType::Sint32);
|
||||||
|
ENUM_NAME(DxbcRegisterComponentType::Float32);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcInstructionReturnType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcInstructionReturnType::Float);
|
||||||
|
ENUM_NAME(DxbcInstructionReturnType::Uint);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcSystemValue e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcSystemValue::None);
|
||||||
|
ENUM_NAME(DxbcSystemValue::Position);
|
||||||
|
ENUM_NAME(DxbcSystemValue::ClipDistance);
|
||||||
|
ENUM_NAME(DxbcSystemValue::CullDistance);
|
||||||
|
ENUM_NAME(DxbcSystemValue::RenderTargetId);
|
||||||
|
ENUM_NAME(DxbcSystemValue::ViewportId);
|
||||||
|
ENUM_NAME(DxbcSystemValue::VertexId);
|
||||||
|
ENUM_NAME(DxbcSystemValue::PrimitiveId);
|
||||||
|
ENUM_NAME(DxbcSystemValue::InstanceId);
|
||||||
|
ENUM_NAME(DxbcSystemValue::IsFrontFace);
|
||||||
|
ENUM_NAME(DxbcSystemValue::SampleIndex);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadUeq0EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadVeq0EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadUeq1EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadVeq1EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadUInsideTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalQuadVInsideTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalTriUeq0EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalTriVeq0EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalTriWeq0EdgeTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalTriInsideTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalLineDetailTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::FinalLineDensityTessFactor);
|
||||||
|
ENUM_NAME(DxbcSystemValue::Target);
|
||||||
|
ENUM_NAME(DxbcSystemValue::Depth);
|
||||||
|
ENUM_NAME(DxbcSystemValue::Coverage);
|
||||||
|
ENUM_NAME(DxbcSystemValue::DepthGe);
|
||||||
|
ENUM_NAME(DxbcSystemValue::DepthLe);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, dxvk::DxbcProgramType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcProgramType::PixelShader);
|
||||||
|
ENUM_NAME(DxbcProgramType::VertexShader);
|
||||||
|
ENUM_NAME(DxbcProgramType::GeometryShader);
|
||||||
|
ENUM_NAME(DxbcProgramType::HullShader);
|
||||||
|
ENUM_NAME(DxbcProgramType::DomainShader);
|
||||||
|
ENUM_NAME(DxbcProgramType::ComputeShader);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, dxvk::DxbcCustomDataClass e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcCustomDataClass::Comment);
|
||||||
|
ENUM_NAME(DxbcCustomDataClass::DebugInfo);
|
||||||
|
ENUM_NAME(DxbcCustomDataClass::Opaque);
|
||||||
|
ENUM_NAME(DxbcCustomDataClass::ImmConstBuf);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, dxvk::DxbcScalarType e) {
|
||||||
|
switch (e) {
|
||||||
|
ENUM_NAME(DxbcScalarType::Uint32);
|
||||||
|
ENUM_NAME(DxbcScalarType::Uint64);
|
||||||
|
ENUM_NAME(DxbcScalarType::Sint32);
|
||||||
|
ENUM_NAME(DxbcScalarType::Sint64);
|
||||||
|
ENUM_NAME(DxbcScalarType::Float32);
|
||||||
|
ENUM_NAME(DxbcScalarType::Float64);
|
||||||
|
ENUM_NAME(DxbcScalarType::Bool);
|
||||||
|
ENUM_DEFAULT(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} //namespace dxvk
|
26
src/dxbc/dxbc_names.h
Normal file
26
src/dxbc/dxbc_names.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
#include "dxbc_enums.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOpcode e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcExtOpcode e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOperandType e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOperandExt e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcComponentCount e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcRegMode e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcOperandIndexRepresentation e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcResourceDim e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcResourceReturnType e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcRegisterComponentType e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcInstructionReturnType e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcSystemValue e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcProgramType e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcCustomDataClass e);
|
||||||
|
std::ostream& operator << (std::ostream& os, DxbcScalarType e);
|
||||||
|
|
||||||
|
} // namespace dxvk
|
60
src/dxbc/dxbc_options.cpp
Normal file
60
src/dxbc/dxbc_options.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//#include "../d3d11/d3d11_options.h"
|
||||||
|
|
||||||
|
#include "dxbc_options.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcOptions::DxbcOptions() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcOptions::DxbcOptions(/*const Rc<DxvkDevice>& device, */const D3D11Options& options) {
|
||||||
|
/*const Rc<DxvkAdapter> adapter = device->adapter();
|
||||||
|
|
||||||
|
const DxvkDeviceFeatures& devFeatures = device->features();
|
||||||
|
const DxvkDeviceInfo& devInfo = adapter->devicePropertiesExt();
|
||||||
|
|
||||||
|
useDepthClipWorkaround
|
||||||
|
= !devFeatures.extDepthClipEnable.depthClipEnable;
|
||||||
|
useSubgroupOpsForAtomicCounters
|
||||||
|
= (devInfo.vk11.subgroupSupportedStages & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||||
|
&& (devInfo.vk11.subgroupSupportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT);
|
||||||
|
|
||||||
|
VkFormatFeatureFlags2 r32Features
|
||||||
|
= device->getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal
|
||||||
|
& device->getFormatFeatures(VK_FORMAT_R32_UINT).optimal
|
||||||
|
& device->getFormatFeatures(VK_FORMAT_R32_SINT).optimal;
|
||||||
|
|
||||||
|
supportsTypedUavLoadR32 = (r32Features & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT);
|
||||||
|
|
||||||
|
switch (device->config().useRawSsbo) {
|
||||||
|
case Tristate::Auto: minSsboAlignment = devInfo.core.properties.limits.minStorageBufferOffsetAlignment; break;
|
||||||
|
case Tristate::True: minSsboAlignment = 4u; break;
|
||||||
|
case Tristate::False: minSsboAlignment = ~0u; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invariantPosition = options.invariantPosition;
|
||||||
|
zeroInitWorkgroupMemory = options.zeroInitWorkgroupMemory;
|
||||||
|
forceVolatileTgsmAccess = options.forceVolatileTgsmAccess;
|
||||||
|
disableMsaa = options.disableMsaa;
|
||||||
|
forceSampleRateShading = options.forceSampleRateShading;
|
||||||
|
enableSampleShadingInterlock = device->features().extFragmentShaderInterlock.fragmentShaderSampleInterlock;
|
||||||
|
|
||||||
|
// Figure out float control flags to match D3D11 rules
|
||||||
|
if (options.floatControls) {
|
||||||
|
if (devInfo.vk12.shaderSignedZeroInfNanPreserveFloat32)
|
||||||
|
floatControl.set(DxbcFloatControlFlag::PreserveNan32);
|
||||||
|
if (devInfo.vk12.shaderSignedZeroInfNanPreserveFloat64)
|
||||||
|
floatControl.set(DxbcFloatControlFlag::PreserveNan64);
|
||||||
|
|
||||||
|
if (devInfo.vk12.denormBehaviorIndependence != VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE) {
|
||||||
|
if (devInfo.vk12.shaderDenormFlushToZeroFloat32)
|
||||||
|
floatControl.set(DxbcFloatControlFlag::DenormFlushToZero32);
|
||||||
|
if (devInfo.vk12.shaderDenormPreserveFloat64)
|
||||||
|
floatControl.set(DxbcFloatControlFlag::DenormPreserve64);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/dxbc/dxbc_options.h
Normal file
63
src/dxbc/dxbc_options.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include "util_flags.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct D3D11Options;
|
||||||
|
|
||||||
|
enum class DxbcFloatControlFlag : uint32_t {
|
||||||
|
DenormFlushToZero32,
|
||||||
|
DenormPreserve64,
|
||||||
|
PreserveNan32,
|
||||||
|
PreserveNan64,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxbcFloatControlFlags = Flags<DxbcFloatControlFlag>;
|
||||||
|
|
||||||
|
struct DxbcOptions {
|
||||||
|
DxbcOptions();
|
||||||
|
DxbcOptions(const D3D11Options& options);
|
||||||
|
|
||||||
|
// Clamp oDepth in fragment shaders if the depth
|
||||||
|
// clip device feature is not supported
|
||||||
|
bool useDepthClipWorkaround = false;
|
||||||
|
|
||||||
|
/// Determines whether format qualifiers
|
||||||
|
/// on typed UAV loads are required
|
||||||
|
bool supportsTypedUavLoadR32 = false;
|
||||||
|
|
||||||
|
/// Use subgroup operations to reduce the number of
|
||||||
|
/// atomic operations for append/consume buffers.
|
||||||
|
bool useSubgroupOpsForAtomicCounters = false;
|
||||||
|
|
||||||
|
/// Clear thread-group shared memory to zero
|
||||||
|
bool zeroInitWorkgroupMemory = false;
|
||||||
|
|
||||||
|
/// Declare vertex positions as invariant
|
||||||
|
bool invariantPosition = false;
|
||||||
|
|
||||||
|
/// Insert memory barriers after TGSM stoes
|
||||||
|
bool forceVolatileTgsmAccess = false;
|
||||||
|
|
||||||
|
/// Replace ld_ms with ld
|
||||||
|
bool disableMsaa = false;
|
||||||
|
|
||||||
|
/// Force sample rate shading by using sample
|
||||||
|
/// interpolation for fragment shader inputs
|
||||||
|
bool forceSampleRateShading = false;
|
||||||
|
|
||||||
|
// Enable per-sample interlock if supported
|
||||||
|
bool enableSampleShadingInterlock = false;
|
||||||
|
|
||||||
|
/// Float control flags
|
||||||
|
DxbcFloatControlFlags floatControl;
|
||||||
|
|
||||||
|
/// Minimum storage buffer alignment
|
||||||
|
VkDeviceSize minSsboAlignment = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
58
src/dxbc/dxbc_reader.cpp
Normal file
58
src/dxbc/dxbc_reader.cpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "dxbc_reader.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxbcTag DxbcReader::readTag() {
|
||||||
|
DxbcTag tag;
|
||||||
|
this->read(&tag, 4);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string DxbcReader::readString() {
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
while (m_data[m_pos] != '\0')
|
||||||
|
result.push_back(m_data[m_pos++]);
|
||||||
|
|
||||||
|
m_pos++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcReader::read(void* dst, size_t n) {
|
||||||
|
if (m_pos + n > m_size)
|
||||||
|
throw DxvkError("DxbcReader::read: Unexpected end of file");
|
||||||
|
std::memcpy(dst, m_data + m_pos, n);
|
||||||
|
m_pos += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcReader::skip(size_t n) {
|
||||||
|
if (m_pos + n > m_size)
|
||||||
|
throw DxvkError("DxbcReader::skip: Unexpected end of file");
|
||||||
|
m_pos += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcReader DxbcReader::clone(size_t pos) const {
|
||||||
|
if (pos > m_size)
|
||||||
|
throw DxvkError("DxbcReader::clone: Invalid offset");
|
||||||
|
return DxbcReader(m_data + pos, m_size - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxbcReader DxbcReader::resize(size_t size) const {
|
||||||
|
if (size > m_size)
|
||||||
|
throw DxvkError("DxbcReader::resize: Invalid size");
|
||||||
|
return DxbcReader(m_data, size, m_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxbcReader::store(std::ostream&& stream) const {
|
||||||
|
stream.write(m_data, m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
src/dxbc/dxbc_reader.h
Normal file
78
src/dxbc/dxbc_reader.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "dxbc_tag.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DXBC bytecode reader
|
||||||
|
*
|
||||||
|
* Holds references to the shader byte code and
|
||||||
|
* provides methods to read
|
||||||
|
*/
|
||||||
|
class DxbcReader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcReader(const char* data, size_t size)
|
||||||
|
: DxbcReader(data, size, 0) { }
|
||||||
|
|
||||||
|
auto readu8 () { return this->readNum<uint8_t> (); }
|
||||||
|
auto readu16() { return this->readNum<uint16_t>(); }
|
||||||
|
auto readu32() { return this->readNum<uint32_t>(); }
|
||||||
|
auto readu64() { return this->readNum<uint64_t>(); }
|
||||||
|
|
||||||
|
auto readi8 () { return this->readNum<int8_t> (); }
|
||||||
|
auto readi16() { return this->readNum<int16_t> (); }
|
||||||
|
auto readi32() { return this->readNum<int32_t> (); }
|
||||||
|
auto readi64() { return this->readNum<int64_t> (); }
|
||||||
|
|
||||||
|
auto readf32() { return this->readNum<float> (); }
|
||||||
|
auto readf64() { return this->readNum<double> (); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
auto readEnum() {
|
||||||
|
using Tx = std::underlying_type_t<T>;
|
||||||
|
return static_cast<T>(this->readNum<Tx>());
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcTag readTag();
|
||||||
|
|
||||||
|
std::string readString();
|
||||||
|
|
||||||
|
void read(void* dst, size_t n);
|
||||||
|
|
||||||
|
void skip(size_t n);
|
||||||
|
|
||||||
|
DxbcReader clone(size_t pos) const;
|
||||||
|
|
||||||
|
DxbcReader resize(size_t size) const;
|
||||||
|
|
||||||
|
bool eof() const {
|
||||||
|
return m_pos >= m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void store(std::ostream&& stream) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcReader(const char* data, size_t size, size_t pos)
|
||||||
|
: m_data(data), m_size(size), m_pos(pos) { }
|
||||||
|
|
||||||
|
const char* m_data = nullptr;
|
||||||
|
size_t m_size = 0;
|
||||||
|
size_t m_pos = 0;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T readNum() {
|
||||||
|
T result;
|
||||||
|
this->read(&result, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
47
src/dxbc/dxbc_tag.h
Normal file
47
src/dxbc/dxbc_tag.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Four-character tag
|
||||||
|
*
|
||||||
|
* Used to identify chunks in the
|
||||||
|
* compiled DXBC file by name.
|
||||||
|
*/
|
||||||
|
class DxbcTag {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxbcTag() {
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
m_chars[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
DxbcTag(const char* tag) {
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
m_chars[i] = tag[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const DxbcTag& other) const {
|
||||||
|
bool result = true;
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
result &= m_chars[i] == other.m_chars[i];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const DxbcTag& other) const {
|
||||||
|
return !this->operator == (other);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* operator & () const { return m_chars; }
|
||||||
|
char* operator & () { return m_chars; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
char m_chars[4];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
src/dxbc/dxbc_util.cpp
Normal file
26
src/dxbc/dxbc_util.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include "dxbc_util.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
uint32_t primitiveVertexCount(DxbcPrimitive primitive) {
|
||||||
|
static const std::array<uint32_t, 8> s_vertexCounts = {
|
||||||
|
0, // Undefined
|
||||||
|
1, // Point
|
||||||
|
2, // Line
|
||||||
|
3, // Triangle
|
||||||
|
0, // Undefined
|
||||||
|
0, // Undefined
|
||||||
|
4, // Line with adjacency
|
||||||
|
6, // Triangle with adjacency
|
||||||
|
};
|
||||||
|
|
||||||
|
if (primitive >= DxbcPrimitive::Patch1) {
|
||||||
|
return uint32_t(primitive)
|
||||||
|
- uint32_t(DxbcPrimitive::Patch1)
|
||||||
|
+ 1u;
|
||||||
|
} else {
|
||||||
|
return s_vertexCounts.at(uint32_t(primitive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
127
src/dxbc/dxbc_util.h
Normal file
127
src/dxbc/dxbc_util.h
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dxbc_common.h"
|
||||||
|
#include "dxbc_enums.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Push constant struct
|
||||||
|
*/
|
||||||
|
struct DxbcPushConstants {
|
||||||
|
uint32_t rasterizerSampleCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Binding numbers and properties
|
||||||
|
*/
|
||||||
|
enum DxbcBindingProperties : uint32_t {
|
||||||
|
DxbcConstBufBindingIndex = 0,
|
||||||
|
DxbcConstBufBindingCount = 16,
|
||||||
|
DxbcSamplerBindingIndex = DxbcConstBufBindingIndex
|
||||||
|
+ DxbcConstBufBindingCount,
|
||||||
|
DxbcSamplerBindingCount = 16,
|
||||||
|
DxbcResourceBindingIndex = DxbcSamplerBindingIndex
|
||||||
|
+ DxbcSamplerBindingCount,
|
||||||
|
DxbcResourceBindingCount = 128,
|
||||||
|
DxbcStageBindingCount = DxbcConstBufBindingCount
|
||||||
|
+ DxbcSamplerBindingCount
|
||||||
|
+ DxbcResourceBindingCount,
|
||||||
|
DxbcUavBindingIndex = DxbcStageBindingCount * 6,
|
||||||
|
DxbcUavBindingCount = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes first binding index for a given stage
|
||||||
|
*
|
||||||
|
* \param [in] stage The shader stage
|
||||||
|
* \returns Index of first binding
|
||||||
|
*/
|
||||||
|
inline uint32_t computeStageBindingOffset(DxbcProgramType stage) {
|
||||||
|
return DxbcStageBindingCount * uint32_t(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes first UAV binding index offset for a given stage
|
||||||
|
*
|
||||||
|
* \param [in] stage The shader stage
|
||||||
|
* \returns Index of first UAV binding
|
||||||
|
*/
|
||||||
|
inline uint32_t computeStageUavBindingOffset(DxbcProgramType stage) {
|
||||||
|
return DxbcUavBindingIndex
|
||||||
|
+ DxbcUavBindingCount * (stage == DxbcProgramType::ComputeShader ? 2 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes constant buffer binding index
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] index Constant buffer index
|
||||||
|
* \returns Binding index
|
||||||
|
*/
|
||||||
|
inline uint32_t computeConstantBufferBinding(DxbcProgramType stage, uint32_t index) {
|
||||||
|
return computeStageBindingOffset(stage) + DxbcConstBufBindingIndex + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes sampler binding index
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] index Sampler index
|
||||||
|
* \returns Binding index
|
||||||
|
*/
|
||||||
|
inline uint32_t computeSamplerBinding(DxbcProgramType stage, uint32_t index) {
|
||||||
|
return computeStageBindingOffset(stage) + DxbcSamplerBindingIndex + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes resource binding index
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] index Resource index
|
||||||
|
* \returns Binding index
|
||||||
|
*/
|
||||||
|
inline uint32_t computeSrvBinding(DxbcProgramType stage, uint32_t index) {
|
||||||
|
return computeStageBindingOffset(stage) + DxbcResourceBindingIndex + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes UAV binding offset
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] index UAV index
|
||||||
|
* \returns Binding index
|
||||||
|
*/
|
||||||
|
inline uint32_t computeUavBinding(DxbcProgramType stage, uint32_t index) {
|
||||||
|
return computeStageUavBindingOffset(stage) + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes UAV counter binding offset
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] index UAV index
|
||||||
|
* \returns Binding index
|
||||||
|
*/
|
||||||
|
inline uint32_t computeUavCounterBinding(DxbcProgramType stage, uint32_t index) {
|
||||||
|
return computeStageUavBindingOffset(stage) + DxbcUavBindingCount + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Primitive vertex count
|
||||||
|
*
|
||||||
|
* Calculates the number of vertices
|
||||||
|
* for a given primitive type.
|
||||||
|
*/
|
||||||
|
uint32_t primitiveVertexCount(
|
||||||
|
DxbcPrimitive primitive);
|
||||||
|
|
||||||
|
}
|
607
src/dxbc/dxvk_shader.h
Normal file
607
src/dxbc/dxvk_shader.h
Normal file
|
@ -0,0 +1,607 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dxvk_include.h"
|
||||||
|
#include "dxvk_limits.h"
|
||||||
|
#include "dxvk_pipelayout.h"
|
||||||
|
#include "dxvk_shader_key.h"
|
||||||
|
|
||||||
|
#include "../spirv/spirv_code_buffer.h"
|
||||||
|
#include "../spirv/spirv_compression.h"
|
||||||
|
#include "../spirv/spirv_module.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
class DxvkShader;
|
||||||
|
class DxvkShaderModule;
|
||||||
|
class DxvkPipelineManager;
|
||||||
|
struct DxvkPipelineStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader flags
|
||||||
|
*
|
||||||
|
* Provides extra information about the features
|
||||||
|
* used by a shader.
|
||||||
|
*/
|
||||||
|
enum DxvkShaderFlag : uint64_t {
|
||||||
|
HasSampleRateShading,
|
||||||
|
HasTransformFeedback,
|
||||||
|
ExportsPosition,
|
||||||
|
ExportsStencilRef,
|
||||||
|
ExportsViewportIndexLayerFromVertexStage,
|
||||||
|
ExportsSampleMask,
|
||||||
|
UsesFragmentCoverage,
|
||||||
|
UsesSparseResidency,
|
||||||
|
};
|
||||||
|
|
||||||
|
using DxvkShaderFlags = Flags<DxvkShaderFlag>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader info
|
||||||
|
*/
|
||||||
|
struct DxvkShaderCreateInfo {
|
||||||
|
/// Shader stage
|
||||||
|
VkShaderStageFlagBits stage;
|
||||||
|
/// Descriptor info
|
||||||
|
uint32_t bindingCount = 0;
|
||||||
|
const DxvkBindingInfo* bindings = nullptr;
|
||||||
|
/// Input and output register mask
|
||||||
|
uint32_t inputMask = 0;
|
||||||
|
uint32_t outputMask = 0;
|
||||||
|
/// Flat shading input mask
|
||||||
|
uint32_t flatShadingInputs = 0;
|
||||||
|
/// Push constant range
|
||||||
|
uint32_t pushConstOffset = 0;
|
||||||
|
uint32_t pushConstSize = 0;
|
||||||
|
/// Uniform buffer data
|
||||||
|
uint32_t uniformSize = 0;
|
||||||
|
const char* uniformData = nullptr;
|
||||||
|
/// Rasterized stream, or -1
|
||||||
|
int32_t xfbRasterizedStream = 0;
|
||||||
|
/// Tess control patch vertex count
|
||||||
|
uint32_t patchVertexCount = 0;
|
||||||
|
/// Transform feedback vertex strides
|
||||||
|
uint32_t xfbStrides[MaxNumXfbBuffers] = { };
|
||||||
|
/// Output primitive topology
|
||||||
|
VkPrimitiveTopology outputTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader module create info
|
||||||
|
*/
|
||||||
|
struct DxvkShaderModuleCreateInfo {
|
||||||
|
bool fsDualSrcBlend = false;
|
||||||
|
bool fsFlatShading = false;
|
||||||
|
uint32_t undefinedInputs = 0;
|
||||||
|
|
||||||
|
std::array<VkComponentMapping, MaxNumRenderTargets> rtSwizzles = { };
|
||||||
|
|
||||||
|
bool eq(const DxvkShaderModuleCreateInfo& other) const;
|
||||||
|
|
||||||
|
size_t hash() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader object
|
||||||
|
*
|
||||||
|
* Stores a SPIR-V shader and information on the
|
||||||
|
* bindings that the shader uses. In order to use
|
||||||
|
* the shader with a pipeline, a shader module
|
||||||
|
* needs to be created from he shader object.
|
||||||
|
*/
|
||||||
|
class DxvkShader : public RcObject {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxvkShader(
|
||||||
|
const DxvkShaderCreateInfo& info,
|
||||||
|
SpirvCodeBuffer&& spirv);
|
||||||
|
|
||||||
|
~DxvkShader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader info
|
||||||
|
* \returns Shader info
|
||||||
|
*/
|
||||||
|
const DxvkShaderCreateInfo& info() const {
|
||||||
|
return m_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves shader flags
|
||||||
|
* \returns Shader flags
|
||||||
|
*/
|
||||||
|
DxvkShaderFlags flags() const {
|
||||||
|
return m_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves binding layout
|
||||||
|
* \returns Binding layout
|
||||||
|
*/
|
||||||
|
const DxvkBindingLayout& getBindings() const {
|
||||||
|
return m_bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves spec constant mask
|
||||||
|
* \returns Bit mask of used spec constants
|
||||||
|
*/
|
||||||
|
uint32_t getSpecConstantMask() const {
|
||||||
|
return m_specConstantMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tests whether this shader needs to be compiled
|
||||||
|
*
|
||||||
|
* If pipeline libraries are supported, this will return
|
||||||
|
* \c false once the pipeline library is being compiled.
|
||||||
|
* \returns \c true if compilation is still needed
|
||||||
|
*/
|
||||||
|
bool needsLibraryCompile() const {
|
||||||
|
return m_needsLibraryCompile.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Notifies library compile
|
||||||
|
*
|
||||||
|
* Called automatically when pipeline compilation begins.
|
||||||
|
* Subsequent calls to \ref needsLibraryCompile will return
|
||||||
|
* \c false.
|
||||||
|
*/
|
||||||
|
void notifyLibraryCompile() {
|
||||||
|
m_needsLibraryCompile.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets raw code without modification
|
||||||
|
*/
|
||||||
|
SpirvCodeBuffer getRawCode() const {
|
||||||
|
return m_code.decompress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Patches code using given info
|
||||||
|
*
|
||||||
|
* Rewrites binding IDs and potentially fixes up other
|
||||||
|
* parts of the code depending on pipeline state.
|
||||||
|
* \param [in] layout Biding layout
|
||||||
|
* \param [in] state Pipeline state info
|
||||||
|
* \returns Uncompressed SPIR-V code buffer
|
||||||
|
*/
|
||||||
|
SpirvCodeBuffer getCode(
|
||||||
|
const DxvkBindingLayoutObjects* layout,
|
||||||
|
const DxvkShaderModuleCreateInfo& state) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tests whether this shader supports pipeline libraries
|
||||||
|
*
|
||||||
|
* This is true for any vertex, fragment, or compute shader that does not
|
||||||
|
* require additional pipeline state to be compiled into something useful.
|
||||||
|
* \param [in] standalone Set to \c true to evaluate this in the context
|
||||||
|
* of a single-shader pipeline library, or \c false for a pre-raster
|
||||||
|
* shader library consisting of multiple shader stages.
|
||||||
|
* \returns \c true if this shader can be used with pipeline libraries
|
||||||
|
*/
|
||||||
|
bool canUsePipelineLibrary(bool standalone) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Dumps SPIR-V shader
|
||||||
|
*
|
||||||
|
* Can be used to store the SPIR-V code in a file.
|
||||||
|
* \param [in] outputStream Stream to write to
|
||||||
|
*/
|
||||||
|
void dump(std::ostream& outputStream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets the shader key
|
||||||
|
* \param [in] key Unique key
|
||||||
|
*/
|
||||||
|
void setShaderKey(const DxvkShaderKey& key) {
|
||||||
|
m_key = key;
|
||||||
|
m_hash = key.hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves shader key
|
||||||
|
* \returns The unique shader key
|
||||||
|
*/
|
||||||
|
DxvkShaderKey getShaderKey() const {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get lookup hash
|
||||||
|
*
|
||||||
|
* Retrieves a non-unique hash value derived from the
|
||||||
|
* shader key which can be used to perform lookups.
|
||||||
|
* This is better than relying on the pointer value.
|
||||||
|
* \returns Hash value for map lookups
|
||||||
|
*/
|
||||||
|
size_t getHash() const {
|
||||||
|
return m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves debug name
|
||||||
|
* \returns The shader's name
|
||||||
|
*/
|
||||||
|
std::string debugName() const {
|
||||||
|
return m_key.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get lookup hash for a shader
|
||||||
|
*
|
||||||
|
* Convenience method that returns \c 0 for a null
|
||||||
|
* pointer, and the shader's lookup hash otherwise.
|
||||||
|
* \param [in] shader The shader
|
||||||
|
* \returns The shader's lookup hash, or 0
|
||||||
|
*/
|
||||||
|
static size_t getHash(const Rc<DxvkShader>& shader) {
|
||||||
|
return shader != nullptr ? shader->getHash() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct BindingOffsets {
|
||||||
|
uint32_t bindingId;
|
||||||
|
uint32_t bindingOffset;
|
||||||
|
uint32_t setOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
DxvkShaderCreateInfo m_info;
|
||||||
|
SpirvCompressedBuffer m_code;
|
||||||
|
|
||||||
|
DxvkShaderFlags m_flags;
|
||||||
|
DxvkShaderKey m_key;
|
||||||
|
size_t m_hash = 0;
|
||||||
|
|
||||||
|
size_t m_o1IdxOffset = 0;
|
||||||
|
size_t m_o1LocOffset = 0;
|
||||||
|
|
||||||
|
uint32_t m_specConstantMask = 0;
|
||||||
|
std::atomic<bool> m_needsLibraryCompile = { true };
|
||||||
|
|
||||||
|
std::vector<char> m_uniformData;
|
||||||
|
std::vector<BindingOffsets> m_bindingOffsets;
|
||||||
|
|
||||||
|
DxvkBindingLayout m_bindings;
|
||||||
|
|
||||||
|
static void eliminateInput(
|
||||||
|
SpirvCodeBuffer& code,
|
||||||
|
uint32_t location);
|
||||||
|
|
||||||
|
static void emitOutputSwizzles(
|
||||||
|
SpirvCodeBuffer& code,
|
||||||
|
uint32_t outputMask,
|
||||||
|
const VkComponentMapping* swizzles);
|
||||||
|
|
||||||
|
static void emitFlatShadingDeclarations(
|
||||||
|
SpirvCodeBuffer& code,
|
||||||
|
uint32_t inputMask);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader module object
|
||||||
|
*
|
||||||
|
* Manages a Vulkan shader module. This will not
|
||||||
|
* perform any shader compilation. Instead, the
|
||||||
|
* context will create pipeline objects on the
|
||||||
|
* fly when executing draw calls.
|
||||||
|
*/
|
||||||
|
class DxvkShaderStageInfo {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxvkShaderStageInfo(const DxvkDevice* device);
|
||||||
|
|
||||||
|
DxvkShaderStageInfo (DxvkShaderStageInfo&& other) = delete;
|
||||||
|
DxvkShaderStageInfo& operator = (DxvkShaderStageInfo&& other) = delete;
|
||||||
|
|
||||||
|
~DxvkShaderStageInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Counts shader stages
|
||||||
|
* \returns Shader stage count
|
||||||
|
*/
|
||||||
|
uint32_t getStageCount() const {
|
||||||
|
return m_stageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Queries shader stage infos
|
||||||
|
* \returns Pointer to shader stage infos
|
||||||
|
*/
|
||||||
|
const VkPipelineShaderStageCreateInfo* getStageInfos() const {
|
||||||
|
return m_stageInfos.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds a shader stage with specialization info
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] code SPIR-V code
|
||||||
|
* \param [in] specinfo Specialization info
|
||||||
|
*/
|
||||||
|
void addStage(
|
||||||
|
VkShaderStageFlagBits stage,
|
||||||
|
SpirvCodeBuffer&& code,
|
||||||
|
const VkSpecializationInfo* specInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds stage using a module identifier
|
||||||
|
*
|
||||||
|
* \param [in] stage Shader stage
|
||||||
|
* \param [in] identifier Shader module identifier
|
||||||
|
* \param [in] specinfo Specialization info
|
||||||
|
*/
|
||||||
|
void addStage(
|
||||||
|
VkShaderStageFlagBits stage,
|
||||||
|
const VkShaderModuleIdentifierEXT& identifier,
|
||||||
|
const VkSpecializationInfo* specInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const DxvkDevice* m_device;
|
||||||
|
|
||||||
|
struct ShaderModuleIdentifier {
|
||||||
|
VkPipelineShaderStageModuleIdentifierCreateInfoEXT createInfo;
|
||||||
|
std::array<uint8_t, VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ShaderModuleInfo {
|
||||||
|
ShaderModuleIdentifier moduleIdentifier;
|
||||||
|
VkShaderModuleCreateInfo moduleInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<SpirvCodeBuffer, 5> m_codeBuffers;
|
||||||
|
std::array<ShaderModuleInfo, 5> m_moduleInfos = { };
|
||||||
|
std::array<VkPipelineShaderStageCreateInfo, 5> m_stageInfos = { };
|
||||||
|
uint32_t m_stageCount = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader pipeline library compile args
|
||||||
|
*/
|
||||||
|
struct DxvkShaderPipelineLibraryCompileArgs {
|
||||||
|
VkBool32 depthClipEnable = VK_TRUE;
|
||||||
|
|
||||||
|
bool operator == (const DxvkShaderPipelineLibraryCompileArgs& other) const {
|
||||||
|
return depthClipEnable == other.depthClipEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const DxvkShaderPipelineLibraryCompileArgs& other) const {
|
||||||
|
return !this->operator == (other);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash() const {
|
||||||
|
return size_t(depthClipEnable);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader set
|
||||||
|
*
|
||||||
|
* Stores a set of shader pointers
|
||||||
|
* for use in a pipeline library.
|
||||||
|
*/
|
||||||
|
struct DxvkShaderSet {
|
||||||
|
DxvkShader* vs = nullptr;
|
||||||
|
DxvkShader* tcs = nullptr;
|
||||||
|
DxvkShader* tes = nullptr;
|
||||||
|
DxvkShader* gs = nullptr;
|
||||||
|
DxvkShader* fs = nullptr;
|
||||||
|
DxvkShader* cs = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader identifer set
|
||||||
|
*
|
||||||
|
* Stores a set of shader module identifiers
|
||||||
|
* for use in a pipeline library.
|
||||||
|
*/
|
||||||
|
struct DxvkShaderIdentifierSet {
|
||||||
|
VkShaderModuleIdentifierEXT vs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
VkShaderModuleIdentifierEXT tcs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
VkShaderModuleIdentifierEXT tes = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
VkShaderModuleIdentifierEXT gs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
VkShaderModuleIdentifierEXT fs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
VkShaderModuleIdentifierEXT cs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader pipeline library key
|
||||||
|
*/
|
||||||
|
class DxvkShaderPipelineLibraryKey {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxvkShaderPipelineLibraryKey();
|
||||||
|
|
||||||
|
~DxvkShaderPipelineLibraryKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates shader set from key
|
||||||
|
* \returns Shader set
|
||||||
|
*/
|
||||||
|
DxvkShaderSet getShaderSet() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Generates merged binding layout
|
||||||
|
* \returns Binding layout
|
||||||
|
*/
|
||||||
|
DxvkBindingLayout getBindings() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds a shader to the key
|
||||||
|
*
|
||||||
|
* Shaders must be added in stage order.
|
||||||
|
* \param [in] shader Shader to add
|
||||||
|
*/
|
||||||
|
void addShader(
|
||||||
|
const Rc<DxvkShader>& shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks wether a pipeline library can be created
|
||||||
|
* \returns \c true if all added shaders are compatible
|
||||||
|
*/
|
||||||
|
bool canUsePipelineLibrary() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks for equality
|
||||||
|
*
|
||||||
|
* \param [in] other Key to compare to
|
||||||
|
* \returns \c true if the keys are equal
|
||||||
|
*/
|
||||||
|
bool eq(
|
||||||
|
const DxvkShaderPipelineLibraryKey& other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes key hash
|
||||||
|
* \returns Key hash
|
||||||
|
*/
|
||||||
|
size_t hash() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t m_shaderCount = 0;
|
||||||
|
VkShaderStageFlags m_shaderStages = 0;
|
||||||
|
std::array<Rc<DxvkShader>, 4> m_shaders;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader pipeline library
|
||||||
|
*
|
||||||
|
* Stores a pipeline object for either a complete compute
|
||||||
|
* pipeline, a pre-rasterization pipeline library consisting
|
||||||
|
* of a single vertex shader, or a fragment shader pipeline
|
||||||
|
* library. All state unknown at shader compile time will
|
||||||
|
* be made dynamic.
|
||||||
|
*/
|
||||||
|
class DxvkShaderPipelineLibrary {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DxvkShaderPipelineLibrary(
|
||||||
|
const DxvkDevice* device,
|
||||||
|
DxvkPipelineManager* manager,
|
||||||
|
const DxvkShaderPipelineLibraryKey& key,
|
||||||
|
const DxvkBindingLayoutObjects* layout);
|
||||||
|
|
||||||
|
~DxvkShaderPipelineLibrary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Queries shader module identifier
|
||||||
|
*
|
||||||
|
* Can be used to compile an optimized pipeline using the same
|
||||||
|
* shader code, but without having to wait for the pipeline
|
||||||
|
* library for this shader shader to compile first.
|
||||||
|
* \param [in] stage Shader stage to query
|
||||||
|
* \returns Shader module identifier
|
||||||
|
*/
|
||||||
|
VkShaderModuleIdentifierEXT getModuleIdentifier(
|
||||||
|
VkShaderStageFlagBits stage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Acquires pipeline handle for the given set of arguments
|
||||||
|
*
|
||||||
|
* Either returns an already compiled pipeline library object, or
|
||||||
|
* performs the compilation step if that has not happened yet.
|
||||||
|
* Increments the use count by one.
|
||||||
|
* \param [in] args Compile arguments
|
||||||
|
* \returns Vulkan pipeline handle
|
||||||
|
*/
|
||||||
|
VkPipeline acquirePipelineHandle(
|
||||||
|
const DxvkShaderPipelineLibraryCompileArgs& args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Releases pipeline
|
||||||
|
*
|
||||||
|
* Decrements the use count by 1. If the use count reaches 0,
|
||||||
|
* any previously compiled pipeline library object may be
|
||||||
|
* destroyed in order to save memory.
|
||||||
|
*/
|
||||||
|
void releasePipelineHandle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compiles the pipeline with default arguments
|
||||||
|
*
|
||||||
|
* This is meant to be called from a worker thread in
|
||||||
|
* order to reduce the amount of work done on the app's
|
||||||
|
* main thread.
|
||||||
|
*/
|
||||||
|
void compilePipeline();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const DxvkDevice* m_device;
|
||||||
|
DxvkPipelineStats* m_stats;
|
||||||
|
DxvkShaderSet m_shaders;
|
||||||
|
const DxvkBindingLayoutObjects* m_layout;
|
||||||
|
|
||||||
|
dxvk::mutex m_mutex;
|
||||||
|
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||||
|
VkPipeline m_pipelineNoDepthClip = VK_NULL_HANDLE;
|
||||||
|
uint32_t m_useCount = 0u;
|
||||||
|
bool m_compiledOnce = false;
|
||||||
|
|
||||||
|
dxvk::mutex m_identifierMutex;
|
||||||
|
DxvkShaderIdentifierSet m_identifiers;
|
||||||
|
|
||||||
|
void destroyShaderPipelinesLocked();
|
||||||
|
|
||||||
|
VkPipeline compileShaderPipelineLocked(
|
||||||
|
const DxvkShaderPipelineLibraryCompileArgs& args);
|
||||||
|
|
||||||
|
VkPipeline compileShaderPipeline(
|
||||||
|
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||||
|
VkPipelineCreateFlags flags);
|
||||||
|
|
||||||
|
VkPipeline compileVertexShaderPipeline(
|
||||||
|
const DxvkShaderPipelineLibraryCompileArgs& args,
|
||||||
|
const DxvkShaderStageInfo& stageInfo,
|
||||||
|
VkPipelineCreateFlags flags);
|
||||||
|
|
||||||
|
VkPipeline compileFragmentShaderPipeline(
|
||||||
|
const DxvkShaderStageInfo& stageInfo,
|
||||||
|
VkPipelineCreateFlags flags);
|
||||||
|
|
||||||
|
VkPipeline compileComputeShaderPipeline(
|
||||||
|
const DxvkShaderStageInfo& stageInfo,
|
||||||
|
VkPipelineCreateFlags flags);
|
||||||
|
|
||||||
|
SpirvCodeBuffer getShaderCode(
|
||||||
|
VkShaderStageFlagBits stage) const;
|
||||||
|
|
||||||
|
void generateModuleIdentifierLocked(
|
||||||
|
VkShaderModuleIdentifierEXT* identifier,
|
||||||
|
const SpirvCodeBuffer& spirvCode);
|
||||||
|
|
||||||
|
VkShaderStageFlags getShaderStages() const;
|
||||||
|
|
||||||
|
DxvkShader* getShader(
|
||||||
|
VkShaderStageFlagBits stage) const;
|
||||||
|
|
||||||
|
VkShaderModuleIdentifierEXT* getShaderIdentifier(
|
||||||
|
VkShaderStageFlagBits stage);
|
||||||
|
|
||||||
|
void notifyLibraryCompile() const;
|
||||||
|
|
||||||
|
bool canUsePipelineCacheControl() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
41
src/dxvk/dxvk_hash.h
Normal file
41
src/dxvk/dxvk_hash.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct DxvkEq {
|
||||||
|
template<typename T>
|
||||||
|
size_t operator () (const T& a, const T& b) const {
|
||||||
|
return a.eq(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
13
src/spirv/CMakeLists.txt
Normal file
13
src/spirv/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
add_library(dxbc-spirv STATIC)
|
||||||
|
target_sources(dxbc-spirv PRIVATE
|
||||||
|
spirv_code_buffer.cpp
|
||||||
|
spirv_code_buffer.h
|
||||||
|
spirv_compression.cpp
|
||||||
|
spirv_compression.h
|
||||||
|
spirv_include.h
|
||||||
|
spirv_instruction.h
|
||||||
|
spirv_module.cpp
|
||||||
|
spirv_module.h
|
||||||
|
)
|
||||||
|
target_include_directories(dxbc-spirv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_link_libraries(dxbc-spirv PUBLIC windows-headers)
|
154
src/spirv/spirv_code_buffer.cpp
Normal file
154
src/spirv/spirv_code_buffer.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "spirv_code_buffer.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
SpirvCodeBuffer:: SpirvCodeBuffer() { }
|
||||||
|
SpirvCodeBuffer::~SpirvCodeBuffer() { }
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size)
|
||||||
|
: m_ptr(size) {
|
||||||
|
m_code.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data)
|
||||||
|
: m_ptr(size) {
|
||||||
|
m_code.resize(size);
|
||||||
|
std::memcpy(m_code.data(), data, size * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCodeBuffer::SpirvCodeBuffer(std::istream& stream) {
|
||||||
|
stream.ignore(std::numeric_limits<std::streamsize>::max());
|
||||||
|
std::streamsize length = stream.gcount();
|
||||||
|
stream.clear();
|
||||||
|
stream.seekg(0, std::ios_base::beg);
|
||||||
|
|
||||||
|
std::vector<char> buffer(length);
|
||||||
|
stream.read(buffer.data(), length);
|
||||||
|
buffer.resize(stream.gcount());
|
||||||
|
|
||||||
|
m_code.resize(buffer.size() / sizeof(uint32_t));
|
||||||
|
std::memcpy(reinterpret_cast<char*>(m_code.data()),
|
||||||
|
buffer.data(), m_code.size() * sizeof(uint32_t));
|
||||||
|
|
||||||
|
m_ptr = m_code.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t SpirvCodeBuffer::allocId() {
|
||||||
|
constexpr size_t BoundIdsOffset = 3;
|
||||||
|
|
||||||
|
if (m_code.size() <= BoundIdsOffset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_code[BoundIdsOffset]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::append(const SpirvCodeBuffer& other) {
|
||||||
|
if (other.size() != 0) {
|
||||||
|
const size_t size = m_code.size();
|
||||||
|
m_code.resize(size + other.m_code.size());
|
||||||
|
|
||||||
|
uint32_t* dst = this->m_code.data();
|
||||||
|
const uint32_t* src = other.m_code.data();
|
||||||
|
|
||||||
|
std::memcpy(dst + size, src, other.size());
|
||||||
|
m_ptr += other.m_code.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putWord(uint32_t word) {
|
||||||
|
m_code.insert(m_code.begin() + m_ptr, word);
|
||||||
|
m_ptr += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putIns(spv::Op opCode, uint16_t wordCount) {
|
||||||
|
this->putWord(
|
||||||
|
(static_cast<uint32_t>(opCode) << 0)
|
||||||
|
| (static_cast<uint32_t>(wordCount) << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putInt32(uint32_t word) {
|
||||||
|
this->putWord(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putInt64(uint64_t value) {
|
||||||
|
this->putWord(value >> 0);
|
||||||
|
this->putWord(value >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putFloat32(float value) {
|
||||||
|
uint32_t tmp;
|
||||||
|
static_assert(sizeof(tmp) == sizeof(value));
|
||||||
|
std::memcpy(&tmp, &value, sizeof(value));
|
||||||
|
this->putInt32(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putFloat64(double value) {
|
||||||
|
uint64_t tmp;
|
||||||
|
static_assert(sizeof(tmp) == sizeof(value));
|
||||||
|
std::memcpy(&tmp, &value, sizeof(value));
|
||||||
|
this->putInt64(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putStr(const char* str) {
|
||||||
|
uint32_t word = 0;
|
||||||
|
uint32_t nbit = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; str[i] != '\0'; str++) {
|
||||||
|
word |= (static_cast<uint32_t>(str[i]) & 0xFF) << nbit;
|
||||||
|
|
||||||
|
if ((nbit += 8) == 32) {
|
||||||
|
this->putWord(word);
|
||||||
|
word = 0;
|
||||||
|
nbit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit current word
|
||||||
|
this->putWord(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::putHeader(uint32_t version, uint32_t boundIds) {
|
||||||
|
this->putWord(spv::MagicNumber);
|
||||||
|
this->putWord(version);
|
||||||
|
this->putWord(0); // Generator
|
||||||
|
this->putWord(boundIds);
|
||||||
|
this->putWord(0); // Schema
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::erase(size_t size) {
|
||||||
|
m_code.erase(
|
||||||
|
m_code.begin() + m_ptr,
|
||||||
|
m_code.begin() + m_ptr + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t SpirvCodeBuffer::strLen(const char* str) {
|
||||||
|
// Null-termination plus padding
|
||||||
|
return (std::strlen(str) + 4) / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpirvCodeBuffer::store(std::ostream& stream) const {
|
||||||
|
stream.write(
|
||||||
|
reinterpret_cast<const char*>(m_code.data()),
|
||||||
|
sizeof(uint32_t) * m_code.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
228
src/spirv/spirv_code_buffer.h
Normal file
228
src/spirv/spirv_code_buffer.h
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "spirv_instruction.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SPIR-V code buffer
|
||||||
|
*
|
||||||
|
* Helper class for generating SPIR-V shaders.
|
||||||
|
* Stores arbitrary SPIR-V instructions in a
|
||||||
|
* format that can be read by Vulkan drivers.
|
||||||
|
*/
|
||||||
|
class SpirvCodeBuffer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpirvCodeBuffer();
|
||||||
|
explicit SpirvCodeBuffer(uint32_t size);
|
||||||
|
SpirvCodeBuffer(const SpirvCodeBuffer &) = default;
|
||||||
|
SpirvCodeBuffer(SpirvCodeBuffer &&) = default;
|
||||||
|
SpirvCodeBuffer(uint32_t size, const uint32_t* data);
|
||||||
|
SpirvCodeBuffer(std::istream& stream);
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
SpirvCodeBuffer(const uint32_t (&data)[N])
|
||||||
|
: SpirvCodeBuffer(N, data) { }
|
||||||
|
|
||||||
|
~SpirvCodeBuffer();
|
||||||
|
|
||||||
|
SpirvCodeBuffer &operator=(const SpirvCodeBuffer &) = default;
|
||||||
|
SpirvCodeBuffer &operator=(SpirvCodeBuffer &&) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Code data
|
||||||
|
* \returns Code data
|
||||||
|
*/
|
||||||
|
const uint32_t* data() const { return m_code.data(); }
|
||||||
|
uint32_t* data() { return m_code.data(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Code size, in dwords
|
||||||
|
* \returns Code size, in dwords
|
||||||
|
*/
|
||||||
|
uint32_t dwords() const {
|
||||||
|
return m_code.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Code size, in bytes
|
||||||
|
* \returns Code size, in bytes
|
||||||
|
*/
|
||||||
|
size_t size() const {
|
||||||
|
return m_code.size() * sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Begin instruction iterator
|
||||||
|
*
|
||||||
|
* Points to the first instruction in the instruction
|
||||||
|
* block. The header, if any, will be skipped over.
|
||||||
|
* \returns Instruction iterator
|
||||||
|
*/
|
||||||
|
SpirvInstructionIterator begin() {
|
||||||
|
return SpirvInstructionIterator(
|
||||||
|
m_code.data(), 0, m_code.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief End instruction iterator
|
||||||
|
*
|
||||||
|
* Points to the end of the instruction block.
|
||||||
|
* \returns Instruction iterator
|
||||||
|
*/
|
||||||
|
SpirvInstructionIterator end() {
|
||||||
|
return SpirvInstructionIterator(nullptr, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocates a new ID
|
||||||
|
*
|
||||||
|
* Returns a new valid ID and increments the
|
||||||
|
* maximum ID count stored in the header.
|
||||||
|
* \returns The new SPIR-V ID
|
||||||
|
*/
|
||||||
|
uint32_t allocId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Merges two code buffers
|
||||||
|
*
|
||||||
|
* This is useful to generate declarations or
|
||||||
|
* the SPIR-V header at the same time as the
|
||||||
|
* code when doing so in advance is impossible.
|
||||||
|
* \param [in] other Code buffer to append
|
||||||
|
*/
|
||||||
|
void append(const SpirvCodeBuffer& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends an 32-bit word to the buffer
|
||||||
|
* \param [in] word The word to append
|
||||||
|
*/
|
||||||
|
void putWord(uint32_t word);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends an instruction word to the buffer
|
||||||
|
*
|
||||||
|
* Adds a single word containing both the word count
|
||||||
|
* and the op code number for a single instruction.
|
||||||
|
* \param [in] opCode Operand code
|
||||||
|
* \param [in] wordCount Number of words
|
||||||
|
*/
|
||||||
|
void putIns(spv::Op opCode, uint16_t wordCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends a 32-bit integer to the buffer
|
||||||
|
* \param [in] value The number to add
|
||||||
|
*/
|
||||||
|
void putInt32(uint32_t word);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends a 64-bit integer to the buffer
|
||||||
|
*
|
||||||
|
* A 64-bit integer will take up two 32-bit words.
|
||||||
|
* \param [in] value 64-bit value to add
|
||||||
|
*/
|
||||||
|
void putInt64(uint64_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends a 32-bit float to the buffer
|
||||||
|
* \param [in] value The number to add
|
||||||
|
*/
|
||||||
|
void putFloat32(float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends a 64-bit float to the buffer
|
||||||
|
* \param [in] value The number to add
|
||||||
|
*/
|
||||||
|
void putFloat64(double value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Appends a literal string to the buffer
|
||||||
|
* \param [in] str String to append to the buffer
|
||||||
|
*/
|
||||||
|
void putStr(const char* str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds the header to the buffer
|
||||||
|
*
|
||||||
|
* \param [in] version SPIR-V version
|
||||||
|
* \param [in] boundIds Number of bound IDs
|
||||||
|
*/
|
||||||
|
void putHeader(uint32_t version, uint32_t boundIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Erases given number of dwords
|
||||||
|
*
|
||||||
|
* Removes data from the code buffer, starting
|
||||||
|
* at the current insertion offset.
|
||||||
|
* \param [in] size Number of words to remove
|
||||||
|
*/
|
||||||
|
void erase(size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes length of a literal string
|
||||||
|
*
|
||||||
|
* \param [in] str The string to check
|
||||||
|
* \returns Number of words consumed by a string
|
||||||
|
*/
|
||||||
|
uint32_t strLen(const char* str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Stores the SPIR-V module to a stream
|
||||||
|
*
|
||||||
|
* The ability to save modules to a file
|
||||||
|
* exists mostly for debugging purposes.
|
||||||
|
* \param [in] stream Output stream
|
||||||
|
*/
|
||||||
|
void store(std::ostream& stream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves current insertion pointer
|
||||||
|
*
|
||||||
|
* Sometimes it may be necessay to insert code into the
|
||||||
|
* middle of the stream rather than appending it. This
|
||||||
|
* retrieves the current function pointer. Note that the
|
||||||
|
* pointer will become invalid if any code is inserted
|
||||||
|
* before the current pointer location.
|
||||||
|
* \returns Current instruction pointr
|
||||||
|
*/
|
||||||
|
size_t getInsertionPtr() const {
|
||||||
|
return m_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets insertion pointer to a specific value
|
||||||
|
*
|
||||||
|
* Sets the insertion pointer to a value that was
|
||||||
|
* previously retrieved by \ref getInsertionPtr.
|
||||||
|
* \returns Current instruction pointr
|
||||||
|
*/
|
||||||
|
void beginInsertion(size_t ptr) {
|
||||||
|
m_ptr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets insertion pointer to the end
|
||||||
|
*
|
||||||
|
* After this call, new instructions will be
|
||||||
|
* appended to the stream. In other words,
|
||||||
|
* this will restore default behaviour.
|
||||||
|
* \returns Previous instruction pointer
|
||||||
|
*/
|
||||||
|
size_t endInsertion() {
|
||||||
|
return std::exchange(m_ptr, m_code.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_code;
|
||||||
|
size_t m_ptr = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
123
src/spirv/spirv_compression.cpp
Normal file
123
src/spirv/spirv_compression.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include "spirv_compression.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
SpirvCompressedBuffer::SpirvCompressedBuffer()
|
||||||
|
: m_size(0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCompressedBuffer::SpirvCompressedBuffer(SpirvCodeBuffer& code)
|
||||||
|
: m_size(code.dwords()) {
|
||||||
|
// The compression (detailed below) achieves roughly 55% of the
|
||||||
|
// original size on average and is very consistent, so an initial
|
||||||
|
// estimate of roughly 58% will be accurate most of the time.
|
||||||
|
const uint32_t* data = code.data();
|
||||||
|
m_code.reserve((m_size * 75) / 128);
|
||||||
|
|
||||||
|
std::array<uint32_t, 16> block;
|
||||||
|
uint32_t blockMask = 0;
|
||||||
|
uint32_t blockOffset = 0;
|
||||||
|
|
||||||
|
// The algorithm used is a simple variable-to-fixed compression that
|
||||||
|
// encodes up to two consecutive SPIR-V tokens into one DWORD using
|
||||||
|
// a small number of different encodings. While not achieving great
|
||||||
|
// compression ratios, the main goal is to allow decompression code
|
||||||
|
// to be fast, with short dependency chains.
|
||||||
|
// Compressed tokens are stored in blocks of 16 DWORDs, each preceeded
|
||||||
|
// by a single DWORD which stores the layout for each DWORD, two bits
|
||||||
|
// each. The supported layouts, are as follows:
|
||||||
|
// 0x0: 1x 32-bit; 0x1: 1x 20-bit + 1x 12-bit
|
||||||
|
// 0x2: 2x 16-bit; 0x3: 1x 12-bit + 1x 20-bit
|
||||||
|
// These layouts are chosen to allow reasonably efficient encoding of
|
||||||
|
// opcode tokens, which usually fit into 20 bits, followed by type IDs,
|
||||||
|
// which tend to be low as well since most types are defined early.
|
||||||
|
for (size_t i = 0; i < m_size; ) {
|
||||||
|
if (likely(i + 1 < m_size)) {
|
||||||
|
uint32_t a = data[i];
|
||||||
|
uint32_t b = data[i + 1];
|
||||||
|
uint32_t schema;
|
||||||
|
uint32_t encode;
|
||||||
|
|
||||||
|
if (std::max(a, b) < (1u << 16)) {
|
||||||
|
schema = 0x2;
|
||||||
|
encode = a | (b << 16);
|
||||||
|
} else if (a < (1u << 20) && b < (1u << 12)) {
|
||||||
|
schema = 0x1;
|
||||||
|
encode = a | (b << 20);
|
||||||
|
} else if (a < (1u << 12) && b < (1u << 20)) {
|
||||||
|
schema = 0x3;
|
||||||
|
encode = a | (b << 12);
|
||||||
|
} else {
|
||||||
|
schema = 0x0;
|
||||||
|
encode = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
block[blockOffset] = encode;
|
||||||
|
blockMask |= schema << (blockOffset << 1);
|
||||||
|
blockOffset += 1;
|
||||||
|
|
||||||
|
i += schema ? 2 : 1;
|
||||||
|
} else {
|
||||||
|
block[blockOffset] = data[i++];
|
||||||
|
blockOffset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(blockOffset == 16) || unlikely(i == m_size)) {
|
||||||
|
m_code.insert(m_code.end(), blockMask);
|
||||||
|
m_code.insert(m_code.end(), block.begin(), block.begin() + blockOffset);
|
||||||
|
|
||||||
|
blockMask = 0;
|
||||||
|
blockOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only shrink the array if we have lots of overhead for some reason.
|
||||||
|
// This should only happen on shaders where our initial estimate was
|
||||||
|
// too small. In general, we want to avoid reallocation here.
|
||||||
|
if (m_code.capacity() > (m_code.size() * 10) / 9)
|
||||||
|
m_code.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCompressedBuffer::~SpirvCompressedBuffer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpirvCodeBuffer SpirvCompressedBuffer::decompress() const {
|
||||||
|
SpirvCodeBuffer code(m_size);
|
||||||
|
uint32_t* data = code.data();
|
||||||
|
|
||||||
|
uint32_t srcOffset = 0;
|
||||||
|
uint32_t dstOffset = 0;
|
||||||
|
|
||||||
|
constexpr uint32_t shiftAmounts = 0x0c101420;
|
||||||
|
|
||||||
|
while (dstOffset < m_size) {
|
||||||
|
uint32_t blockMask = m_code[srcOffset];
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < 16 && dstOffset < m_size; i++) {
|
||||||
|
// Use 64-bit integers for some of the operands so we can
|
||||||
|
// shift by 32 bits and not handle it as a special cases
|
||||||
|
uint32_t schema = (blockMask >> (i << 1)) & 0x3;
|
||||||
|
uint32_t shift = (shiftAmounts >> (schema << 3)) & 0xff;
|
||||||
|
uint64_t mask = ~(~0ull << shift);
|
||||||
|
uint64_t encode = m_code[srcOffset + i + 1];
|
||||||
|
|
||||||
|
data[dstOffset] = encode & mask;
|
||||||
|
|
||||||
|
if (likely(schema))
|
||||||
|
data[dstOffset + 1] = encode >> shift;
|
||||||
|
|
||||||
|
dstOffset += schema ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srcOffset += 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/spirv/spirv_compression.h
Normal file
38
src/spirv/spirv_compression.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "spirv_code_buffer.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compressed SPIR-V code buffer
|
||||||
|
*
|
||||||
|
* Implements a fast in-memory compression
|
||||||
|
* to keep memory footprint low.
|
||||||
|
*/
|
||||||
|
class SpirvCompressedBuffer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpirvCompressedBuffer();
|
||||||
|
|
||||||
|
SpirvCompressedBuffer(SpirvCodeBuffer& code);
|
||||||
|
|
||||||
|
~SpirvCompressedBuffer();
|
||||||
|
|
||||||
|
SpirvCodeBuffer decompress() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
size_t m_size;
|
||||||
|
std::vector<uint32_t> m_code;
|
||||||
|
|
||||||
|
void encodeDword(uint32_t dw);
|
||||||
|
|
||||||
|
uint32_t decodeDword(size_t& offset) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
15
src/spirv/spirv_include.h
Normal file
15
src/spirv/spirv_include.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spirv/unified1/spirv.hpp>
|
||||||
|
#include <spirv/unified1/GLSL.std.450.h>
|
||||||
|
|
||||||
|
#include "../util/log/log.h"
|
||||||
|
#include "../util/log/log_debug.h"
|
||||||
|
|
||||||
|
#include "../util/util_error.h"
|
||||||
|
#include "../util/util_flags.h"
|
||||||
|
#include "../util/util_likely.h"
|
||||||
|
#include "../util/util_string.h"
|
||||||
|
|
||||||
|
#include "../util/rc/util_rc.h"
|
||||||
|
#include "../util/rc/util_rc_ptr.h"
|
158
src/spirv/spirv_instruction.h
Normal file
158
src/spirv/spirv_instruction.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spirv_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SPIR-V instruction
|
||||||
|
*
|
||||||
|
* Helps parsing a single instruction, providing
|
||||||
|
* access to the op code, instruction length and
|
||||||
|
* instruction arguments.
|
||||||
|
*/
|
||||||
|
class SpirvInstruction {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpirvInstruction() { }
|
||||||
|
SpirvInstruction(uint32_t* code, uint32_t offset, uint32_t length)
|
||||||
|
: m_code(code), m_offset(offset), m_length(length) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SPIR-V Op code
|
||||||
|
* \returns The op code
|
||||||
|
*/
|
||||||
|
spv::Op opCode() const {
|
||||||
|
return static_cast<spv::Op>(
|
||||||
|
this->arg(0) & spv::OpCodeMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction length
|
||||||
|
* \returns Number of DWORDs
|
||||||
|
*/
|
||||||
|
uint32_t length() const {
|
||||||
|
return this->arg(0) >> spv::WordCountShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Instruction offset
|
||||||
|
* \returns Offset in DWORDs
|
||||||
|
*/
|
||||||
|
uint32_t offset() const {
|
||||||
|
return m_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Argument value
|
||||||
|
*
|
||||||
|
* Retrieves an argument DWORD. Note that some instructions
|
||||||
|
* take 64-bit arguments which require more than one DWORD.
|
||||||
|
* Arguments start at index 1. Calling this method with an
|
||||||
|
* argument ID of 0 will return the opcode token.
|
||||||
|
* \param [in] idx Argument index, starting at 1
|
||||||
|
* \returns The argument value
|
||||||
|
*/
|
||||||
|
uint32_t arg(uint32_t idx) const {
|
||||||
|
const uint32_t index = m_offset + idx;
|
||||||
|
return index < m_length ? m_code[index] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Argument string
|
||||||
|
*
|
||||||
|
* Retrieves a pointer to a UTF-8-encoded string.
|
||||||
|
* \param [in] idx Argument index, starting at 1
|
||||||
|
* \returns Pointer to the literal string
|
||||||
|
*/
|
||||||
|
const char* chr(uint32_t idx) const {
|
||||||
|
const uint32_t index = m_offset + idx;
|
||||||
|
return index < m_length ? reinterpret_cast<const char*>(&m_code[index]) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Changes the value of an argument
|
||||||
|
*
|
||||||
|
* \param [in] idx Argument index, starting at 1
|
||||||
|
* \param [in] word New argument word
|
||||||
|
*/
|
||||||
|
void setArg(uint32_t idx, uint32_t word) const {
|
||||||
|
if (m_offset + idx < m_length)
|
||||||
|
m_code[m_offset + idx] = word;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t* m_code = nullptr;
|
||||||
|
uint32_t m_offset = 0;
|
||||||
|
uint32_t m_length = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SPIR-V instruction iterator
|
||||||
|
*
|
||||||
|
* Convenient iterator that can be used
|
||||||
|
* to process raw SPIR-V shader code.
|
||||||
|
*/
|
||||||
|
class SpirvInstructionIterator {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpirvInstructionIterator() { }
|
||||||
|
SpirvInstructionIterator(uint32_t* code, uint32_t offset, uint32_t length)
|
||||||
|
: m_code (length != 0 ? code : nullptr),
|
||||||
|
m_offset(length != 0 ? offset : 0),
|
||||||
|
m_length(length) {
|
||||||
|
if ((length >= 5) && (offset == 0) && (m_code[0] == spv::MagicNumber))
|
||||||
|
this->advance(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpirvInstructionIterator& operator ++ () {
|
||||||
|
this->advance(SpirvInstruction(m_code, m_offset, m_length).length());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpirvInstructionIterator operator ++ (int) {
|
||||||
|
SpirvInstructionIterator result = *this;
|
||||||
|
this->advance(SpirvInstruction(m_code, m_offset, m_length).length());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpirvInstruction operator * () const {
|
||||||
|
return SpirvInstruction(m_code, m_offset, m_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const SpirvInstructionIterator& other) const {
|
||||||
|
return this->m_code == other.m_code
|
||||||
|
&& this->m_offset == other.m_offset
|
||||||
|
&& this->m_length == other.m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const SpirvInstructionIterator& other) const {
|
||||||
|
return this->m_code != other.m_code
|
||||||
|
|| this->m_offset != other.m_offset
|
||||||
|
|| this->m_length != other.m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t* m_code = nullptr;
|
||||||
|
uint32_t m_offset = 0;
|
||||||
|
uint32_t m_length = 0;
|
||||||
|
|
||||||
|
void advance(uint32_t n) {
|
||||||
|
if (m_offset + n < m_length) {
|
||||||
|
m_offset += n;
|
||||||
|
} else {
|
||||||
|
m_code = nullptr;
|
||||||
|
m_offset = 0;
|
||||||
|
m_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
3877
src/spirv/spirv_module.cpp
Normal file
3877
src/spirv/spirv_module.cpp
Normal file
File diff suppressed because it is too large
Load diff
1320
src/spirv/spirv_module.h
Normal file
1320
src/spirv/spirv_module.h
Normal file
File diff suppressed because it is too large
Load diff
33
src/util/CMakeLists.txt
Normal file
33
src/util/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
add_library(dxbc-util STATIC)
|
||||||
|
target_sources(dxbc-util PRIVATE
|
||||||
|
com/com_guid.cpp
|
||||||
|
com/com_guid.h
|
||||||
|
com/com_include.h
|
||||||
|
com/com_object.h
|
||||||
|
com/com_pointer.h
|
||||||
|
com/com_private_data.cpp
|
||||||
|
com/com_private_data.h
|
||||||
|
|
||||||
|
log/log_debug.cpp
|
||||||
|
log/log_debug.h
|
||||||
|
log/log.cpp
|
||||||
|
log/log.h
|
||||||
|
|
||||||
|
rc/util_rc_ptr.h
|
||||||
|
rc/util_rc.h
|
||||||
|
|
||||||
|
thread.cpp
|
||||||
|
thread.h
|
||||||
|
util_bit.h
|
||||||
|
util_enum.h
|
||||||
|
util_env.cpp
|
||||||
|
util_env.h
|
||||||
|
util_error.h
|
||||||
|
util_flags.h
|
||||||
|
util_likely.h
|
||||||
|
util_math.h
|
||||||
|
util_string.cpp
|
||||||
|
util_string.h
|
||||||
|
)
|
||||||
|
target_include_directories(dxbc-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_link_libraries(dxbc-util PUBLIC windows-headers)
|
64
src/util/com/com_guid.cpp
Normal file
64
src/util/com/com_guid.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "com_guid.h"
|
||||||
|
|
||||||
|
#include "../log/log.h"
|
||||||
|
|
||||||
|
#include "../../dxvk/dxvk_hash.h"
|
||||||
|
|
||||||
|
#include "../thread.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
struct GuidPair {
|
||||||
|
GuidPair() { };
|
||||||
|
GuidPair(IID a_, IID b_)
|
||||||
|
: a(a_), b(b_) { }
|
||||||
|
|
||||||
|
IID a, b;
|
||||||
|
|
||||||
|
size_t hash() const {
|
||||||
|
return size_t(a.Data1) ^ size_t(b.Data1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eq(const GuidPair& other) const {
|
||||||
|
return a == other.a && b == other.b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dxvk::mutex g_loggedQueryInterfaceErrorMutex;
|
||||||
|
std::unordered_set<GuidPair, DxvkHash, DxvkEq> g_loggedQueryInterfaceErrors;
|
||||||
|
|
||||||
|
bool logQueryInterfaceError(REFIID objectGuid, REFIID requestedGuid) {
|
||||||
|
if (Logger::logLevel() > LogLevel::Warn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::lock_guard lock(g_loggedQueryInterfaceErrorMutex);
|
||||||
|
return g_loggedQueryInterfaceErrors.emplace(objectGuid, requestedGuid).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, REFIID guid) {
|
||||||
|
os << std::hex << std::setfill('0')
|
||||||
|
<< std::setw(8) << guid.Data1 << '-';
|
||||||
|
|
||||||
|
os << std::hex << std::setfill('0')
|
||||||
|
<< std::setw(4) << guid.Data2 << '-';
|
||||||
|
|
||||||
|
os << std::hex << std::setfill('0')
|
||||||
|
<< std::setw(4) << guid.Data3 << '-';
|
||||||
|
|
||||||
|
os << std::hex << std::setfill('0')
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[0])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[1])
|
||||||
|
<< '-'
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[2])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[3])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[4])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[5])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[6])
|
||||||
|
<< std::setw(2) << static_cast<short>(guid.Data4[7]);
|
||||||
|
return os;
|
||||||
|
}
|
21
src/util/com/com_guid.h
Normal file
21
src/util/com/com_guid.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include "com_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether an unknown GUID should be logged
|
||||||
|
*
|
||||||
|
* \param [in] objectGuid GUID of the object that QueryInterface is called on
|
||||||
|
* \param [in] requestGuid Requested unsupported GUID
|
||||||
|
* \returns \c true if the error should be logged
|
||||||
|
*/
|
||||||
|
bool logQueryInterfaceError(REFIID objectGuid, REFIID requestedGuid);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& os, REFIID guid);
|
17
src/util/com/com_include.h
Normal file
17
src/util/com/com_include.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// GCC complains about the COM interfaces
|
||||||
|
// not having virtual destructors
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#endif // __GNUC__
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <unknwn.h>
|
||||||
|
|
||||||
|
// GCC: -std options disable certain keywords
|
||||||
|
// https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
|
||||||
|
#if defined(__WINE__) && !defined(typeof)
|
||||||
|
#define typeof __typeof
|
||||||
|
#endif
|
123
src/util/com/com_object.h
Normal file
123
src/util/com/com_object.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "com_include.h"
|
||||||
|
|
||||||
|
#include "../util_likely.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class NoWrapper : public T {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~NoWrapper() { }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reference-counted COM object
|
||||||
|
*
|
||||||
|
* This can serve as a templated base class for most
|
||||||
|
* COM objects. It implements AddRef and Release from
|
||||||
|
* \c IUnknown, and provides methods to increment and
|
||||||
|
* decrement private references which are not visible
|
||||||
|
* to the application.
|
||||||
|
*
|
||||||
|
* Having two reference counters is sadly necessary
|
||||||
|
* in order to not break games which steal internal
|
||||||
|
* references if the refefence count of an object is
|
||||||
|
+ greater than they expect. DXVK sometimes requires
|
||||||
|
* holding on to objects which the application wants
|
||||||
|
* to delete.
|
||||||
|
*/
|
||||||
|
template<typename Base>
|
||||||
|
class ComObject : public Base {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~ComObject() { }
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE AddRef() {
|
||||||
|
uint32_t refCount = m_refCount++;
|
||||||
|
if (unlikely(!refCount))
|
||||||
|
AddRefPrivate();
|
||||||
|
return refCount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE Release() {
|
||||||
|
uint32_t refCount = --m_refCount;
|
||||||
|
if (unlikely(!refCount))
|
||||||
|
ReleasePrivate();
|
||||||
|
return refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AddRefPrivate() {
|
||||||
|
++m_refPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ReleasePrivate() {
|
||||||
|
uint32_t refPrivate = --m_refPrivate;
|
||||||
|
if (unlikely(!refPrivate)) {
|
||||||
|
m_refPrivate += 0x80000000;
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG GetPrivateRefCount() const {
|
||||||
|
return m_refPrivate.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasLiveReferences() const {
|
||||||
|
return bool(m_refCount.load() | (m_refPrivate.load() & 0x7FFFFFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
std::atomic<uint32_t> m_refCount = { 0ul };
|
||||||
|
std::atomic<uint32_t> m_refPrivate = { 0ul };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Clamped, reference-counted COM object
|
||||||
|
*
|
||||||
|
* This version of ComObject ensures that the reference
|
||||||
|
* count does not wrap around if a release happens at zero.
|
||||||
|
* eg. [m_refCount = 0]
|
||||||
|
* Release()
|
||||||
|
* [m_refCount = 0]
|
||||||
|
* This is a notable quirk of D3D9's COM implementation
|
||||||
|
* and is relied upon by some games.
|
||||||
|
*/
|
||||||
|
template<typename Base>
|
||||||
|
class ComObjectClamp : public ComObject<Base> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE Release() {
|
||||||
|
ULONG refCount = this->m_refCount;
|
||||||
|
if (likely(refCount != 0ul)) {
|
||||||
|
this->m_refCount--;
|
||||||
|
refCount--;
|
||||||
|
|
||||||
|
if (refCount == 0ul)
|
||||||
|
this->ReleasePrivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void InitReturnPtr(T** ptr) {
|
||||||
|
if (ptr != nullptr)
|
||||||
|
*ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
146
src/util/com/com_pointer.h
Normal file
146
src/util/com/com_pointer.h
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "com_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Increment public ref count
|
||||||
|
*
|
||||||
|
* If the pointer is not \c nullptr, this
|
||||||
|
* calls \c AddRef for the given object.
|
||||||
|
* \returns Pointer to the object
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T* ref(T* object) {
|
||||||
|
if (object != nullptr)
|
||||||
|
object->AddRef();
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ref count methods for public references
|
||||||
|
*/
|
||||||
|
template<typename T, bool Public>
|
||||||
|
struct ComRef_ {
|
||||||
|
static void incRef(T* ptr) { ptr->AddRef(); }
|
||||||
|
static void decRef(T* ptr) { ptr->Release(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ref count methods for private references
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct ComRef_<T, false> {
|
||||||
|
static void incRef(T* ptr) { ptr->AddRefPrivate(); }
|
||||||
|
static void decRef(T* ptr) { ptr->ReleasePrivate(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief COM pointer
|
||||||
|
*
|
||||||
|
* Implements automatic reference
|
||||||
|
* counting for COM objects.
|
||||||
|
*/
|
||||||
|
template<typename T, bool Public = true>
|
||||||
|
class Com {
|
||||||
|
using ComRef = ComRef_<T, Public>;
|
||||||
|
public:
|
||||||
|
|
||||||
|
Com() { }
|
||||||
|
Com(std::nullptr_t) { }
|
||||||
|
Com(T* object)
|
||||||
|
: m_ptr(object) {
|
||||||
|
this->incRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Com(const Com& other)
|
||||||
|
: m_ptr(other.m_ptr) {
|
||||||
|
this->incRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Com(Com&& other)
|
||||||
|
: m_ptr(other.m_ptr) {
|
||||||
|
other.m_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com& operator = (T* object) {
|
||||||
|
this->decRef();
|
||||||
|
m_ptr = object;
|
||||||
|
this->incRef();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com& operator = (const Com& other) {
|
||||||
|
other.incRef();
|
||||||
|
this->decRef();
|
||||||
|
m_ptr = other.m_ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com& operator = (Com&& other) {
|
||||||
|
this->decRef();
|
||||||
|
this->m_ptr = other.m_ptr;
|
||||||
|
other.m_ptr = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com& operator = (std::nullptr_t) {
|
||||||
|
this->decRef();
|
||||||
|
m_ptr = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Com() {
|
||||||
|
this->decRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator -> () const {
|
||||||
|
return m_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T** operator & () { return &m_ptr; }
|
||||||
|
T* const* operator & () const { return &m_ptr; }
|
||||||
|
|
||||||
|
template<bool Public_>
|
||||||
|
bool operator == (const Com<T, Public_>& other) const { return m_ptr == other.m_ptr; }
|
||||||
|
template<bool Public_>
|
||||||
|
bool operator != (const Com<T, Public_>& other) const { return m_ptr != other.m_ptr; }
|
||||||
|
|
||||||
|
bool operator == (const T* other) const { return m_ptr == other; }
|
||||||
|
bool operator != (const T* other) const { return m_ptr != other; }
|
||||||
|
|
||||||
|
bool operator == (std::nullptr_t) const { return m_ptr == nullptr; }
|
||||||
|
bool operator != (std::nullptr_t) const { return m_ptr != nullptr; }
|
||||||
|
|
||||||
|
T* ref() const {
|
||||||
|
return dxvk::ref(m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() const {
|
||||||
|
return m_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Com<T, true> pubRef() const { return m_ptr; }
|
||||||
|
Com<T, false> prvRef() const { return m_ptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
T* m_ptr = nullptr;
|
||||||
|
|
||||||
|
void incRef() const {
|
||||||
|
if (m_ptr != nullptr)
|
||||||
|
ComRef::incRef(m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decRef() const {
|
||||||
|
if (m_ptr != nullptr)
|
||||||
|
ComRef::decRef(m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
171
src/util/com/com_private_data.cpp
Normal file
171
src/util/com/com_private_data.cpp
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "com_private_data.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
ComPrivateDataEntry::ComPrivateDataEntry() { }
|
||||||
|
ComPrivateDataEntry::ComPrivateDataEntry(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT size,
|
||||||
|
const void* data)
|
||||||
|
: m_guid(guid),
|
||||||
|
m_type(ComPrivateDataType::Data),
|
||||||
|
m_size(size),
|
||||||
|
m_data(std::malloc(size)) {
|
||||||
|
std::memcpy(m_data, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComPrivateDataEntry::ComPrivateDataEntry(
|
||||||
|
REFGUID guid,
|
||||||
|
const IUnknown* iface)
|
||||||
|
: m_guid (guid),
|
||||||
|
m_type(ComPrivateDataType::Iface),
|
||||||
|
m_iface (const_cast<IUnknown*>(iface)) {
|
||||||
|
if (m_iface)
|
||||||
|
m_iface->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComPrivateDataEntry::~ComPrivateDataEntry() {
|
||||||
|
this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComPrivateDataEntry::ComPrivateDataEntry(ComPrivateDataEntry&& other)
|
||||||
|
: m_guid (other.m_guid),
|
||||||
|
m_type (other.m_type),
|
||||||
|
m_size (other.m_size),
|
||||||
|
m_data (other.m_data),
|
||||||
|
m_iface (other.m_iface) {
|
||||||
|
other.m_guid = __uuidof(IUnknown);
|
||||||
|
other.m_type = ComPrivateDataType::None;
|
||||||
|
other.m_size = 0;
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_iface = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComPrivateDataEntry& ComPrivateDataEntry::operator = (ComPrivateDataEntry&& other) {
|
||||||
|
this->destroy();
|
||||||
|
this->m_guid = other.m_guid;
|
||||||
|
this->m_type = other.m_type;
|
||||||
|
this->m_size = other.m_size;
|
||||||
|
this->m_data = other.m_data;
|
||||||
|
this->m_iface = other.m_iface;
|
||||||
|
|
||||||
|
other.m_guid = __uuidof(IUnknown);
|
||||||
|
other.m_type = ComPrivateDataType::None;
|
||||||
|
other.m_size = 0;
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_iface = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT ComPrivateDataEntry::get(UINT& size, void* data) const {
|
||||||
|
UINT minSize = 0;
|
||||||
|
|
||||||
|
if (m_type == ComPrivateDataType::Iface) minSize = sizeof(IUnknown*);
|
||||||
|
if (m_type == ComPrivateDataType::Data) minSize = m_size;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
size = minSize;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT result = size < minSize
|
||||||
|
? DXGI_ERROR_MORE_DATA
|
||||||
|
: S_OK;
|
||||||
|
|
||||||
|
if (size >= minSize) {
|
||||||
|
if (m_type == ComPrivateDataType::Iface) {
|
||||||
|
if (m_iface)
|
||||||
|
m_iface->AddRef();
|
||||||
|
std::memcpy(data, &m_iface, minSize);
|
||||||
|
} else {
|
||||||
|
std::memcpy(data, m_data, minSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size = minSize;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ComPrivateDataEntry::destroy() {
|
||||||
|
if (m_data)
|
||||||
|
std::free(m_data);
|
||||||
|
if (m_iface)
|
||||||
|
m_iface->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT ComPrivateData::setData(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT size,
|
||||||
|
const void* data) {
|
||||||
|
if (!data) {
|
||||||
|
for (auto it = m_entries.begin(); it != m_entries.end(); ++it) {
|
||||||
|
if (it->hasGuid(guid)) {
|
||||||
|
m_entries.erase(it);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
this->insertEntry(ComPrivateDataEntry(guid, size, data));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT ComPrivateData::setInterface(
|
||||||
|
REFGUID guid,
|
||||||
|
const IUnknown* iface) {
|
||||||
|
this->insertEntry(ComPrivateDataEntry(guid, iface));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT ComPrivateData::getData(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT* size,
|
||||||
|
void* data) {
|
||||||
|
if (!size)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
|
||||||
|
auto entry = this->findEntry(guid);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
*size = 0;
|
||||||
|
return DXGI_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry->get(*size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComPrivateDataEntry* ComPrivateData::findEntry(REFGUID guid) {
|
||||||
|
for (ComPrivateDataEntry& e : m_entries) {
|
||||||
|
if (e.hasGuid(guid))
|
||||||
|
return &e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ComPrivateData::insertEntry(ComPrivateDataEntry&& entry) {
|
||||||
|
ComPrivateDataEntry srcEntry = std::move(entry);
|
||||||
|
ComPrivateDataEntry* dstEntry = this->findEntry(srcEntry.guid());
|
||||||
|
|
||||||
|
if (dstEntry)
|
||||||
|
*dstEntry = std::move(srcEntry);
|
||||||
|
else
|
||||||
|
m_entries.push_back(std::move(srcEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
115
src/util/com/com_private_data.h
Normal file
115
src/util/com/com_private_data.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "com_include.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief COM private data entry type
|
||||||
|
*/
|
||||||
|
enum ComPrivateDataType {
|
||||||
|
None,
|
||||||
|
Data,
|
||||||
|
Iface,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Data entry for private storage
|
||||||
|
* Stores a single private storage item.
|
||||||
|
*/
|
||||||
|
class ComPrivateDataEntry {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ComPrivateDataEntry();
|
||||||
|
ComPrivateDataEntry(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT size,
|
||||||
|
const void* data);
|
||||||
|
ComPrivateDataEntry(
|
||||||
|
REFGUID guid,
|
||||||
|
const IUnknown* iface);
|
||||||
|
~ComPrivateDataEntry();
|
||||||
|
|
||||||
|
ComPrivateDataEntry (ComPrivateDataEntry&& other);
|
||||||
|
ComPrivateDataEntry& operator = (ComPrivateDataEntry&& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief The entry's GUID
|
||||||
|
* \returns The GUID
|
||||||
|
*/
|
||||||
|
REFGUID guid() const {
|
||||||
|
return m_guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether the GUID matches another one
|
||||||
|
*
|
||||||
|
* GUIDs are used to identify private data entries.
|
||||||
|
* \param [in] guid The GUID to compare to
|
||||||
|
* \returns \c true if this entry holds the same GUID
|
||||||
|
*/
|
||||||
|
bool hasGuid(REFGUID guid) const {
|
||||||
|
return m_guid == guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves stored data
|
||||||
|
*
|
||||||
|
* \param [in,out] size Destination buffer size
|
||||||
|
* \param [in] data Appliaction-provided buffer
|
||||||
|
* \returns \c S_OK on success, or \c DXGI_ERROR_MORE_DATA
|
||||||
|
* if the destination buffer is too small
|
||||||
|
*/
|
||||||
|
HRESULT get(UINT& size, void* data) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
GUID m_guid = __uuidof(IUnknown);
|
||||||
|
ComPrivateDataType m_type = ComPrivateDataType::None;
|
||||||
|
UINT m_size = 0;
|
||||||
|
void* m_data = nullptr;
|
||||||
|
IUnknown* m_iface = nullptr;
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Private storage for DXGI objects
|
||||||
|
*
|
||||||
|
* Provides storage for application-defined
|
||||||
|
* byte arrays or COM interfaces that can be
|
||||||
|
* retrieved using GUIDs.
|
||||||
|
*/
|
||||||
|
class ComPrivateData {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
HRESULT setData(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT size,
|
||||||
|
const void* data);
|
||||||
|
|
||||||
|
HRESULT setInterface(
|
||||||
|
REFGUID guid,
|
||||||
|
const IUnknown* iface);
|
||||||
|
|
||||||
|
HRESULT getData(
|
||||||
|
REFGUID guid,
|
||||||
|
UINT* size,
|
||||||
|
void* data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<ComPrivateDataEntry> m_entries;
|
||||||
|
|
||||||
|
ComPrivateDataEntry* findEntry(REFGUID guid);
|
||||||
|
void insertEntry(ComPrivateDataEntry&& entry);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
132
src/util/log/log.cpp
Normal file
132
src/util/log/log.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include "../util_env.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
Logger::Logger(const std::string& fileName)
|
||||||
|
: m_minLevel(getMinLogLevel()), m_fileName(fileName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Logger::~Logger() { }
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::trace(const std::string& message) {
|
||||||
|
s_instance.emitMsg(LogLevel::Trace, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::debug(const std::string& message) {
|
||||||
|
s_instance.emitMsg(LogLevel::Debug, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::info(const std::string& message) {
|
||||||
|
s_instance.emitMsg(LogLevel::Info, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::warn(const std::string& message) {
|
||||||
|
s_instance.emitMsg(LogLevel::Warn, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::err(const std::string& message) {
|
||||||
|
s_instance.emitMsg(LogLevel::Error, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::log(LogLevel level, const std::string& message) {
|
||||||
|
s_instance.emitMsg(level, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Logger::emitMsg(LogLevel level, const std::string& message) {
|
||||||
|
if (level >= m_minLevel) {
|
||||||
|
std::lock_guard<dxvk::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
static std::array<const char*, 5> s_prefixes
|
||||||
|
= {{ "trace: ", "debug: ", "info: ", "warn: ", "err: " }};
|
||||||
|
|
||||||
|
const char* prefix = s_prefixes.at(static_cast<uint32_t>(level));
|
||||||
|
|
||||||
|
if (!std::exchange(m_initialized, true)) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||||
|
|
||||||
|
if (ntdll)
|
||||||
|
m_wineLogOutput = reinterpret_cast<PFN_wineLogOutput>(GetProcAddress(ntdll, "__wine_dbg_output"));
|
||||||
|
#endif
|
||||||
|
auto path = getFileName(m_fileName);
|
||||||
|
|
||||||
|
if (!path.empty())
|
||||||
|
m_fileStream = std::ofstream(str::topath(path.c_str()).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream stream(message);
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(stream, line, '\n')) {
|
||||||
|
std::stringstream outstream;
|
||||||
|
outstream << prefix << line << std::endl;
|
||||||
|
|
||||||
|
std::string adjusted = outstream.str();
|
||||||
|
|
||||||
|
if (!adjusted.empty()) {
|
||||||
|
if (m_wineLogOutput)
|
||||||
|
m_wineLogOutput(adjusted.c_str());
|
||||||
|
else
|
||||||
|
std::cerr << adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_fileStream)
|
||||||
|
m_fileStream << adjusted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Logger::getFileName(const std::string& base) {
|
||||||
|
std::string path = env::getEnvVar("DXVK_LOG_PATH");
|
||||||
|
|
||||||
|
if (path == "none")
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
// Don't create a log file if we're writing to wine's console output
|
||||||
|
if (path.empty() && m_wineLogOutput)
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
if (!path.empty() && *path.rbegin() != '/')
|
||||||
|
path += '/';
|
||||||
|
|
||||||
|
std::string exeName = env::getExeBaseName();
|
||||||
|
path += exeName + "_" + base;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogLevel Logger::getMinLogLevel() {
|
||||||
|
const std::array<std::pair<const char*, LogLevel>, 6> logLevels = {{
|
||||||
|
{ "trace", LogLevel::Trace },
|
||||||
|
{ "debug", LogLevel::Debug },
|
||||||
|
{ "info", LogLevel::Info },
|
||||||
|
{ "warn", LogLevel::Warn },
|
||||||
|
{ "error", LogLevel::Error },
|
||||||
|
{ "none", LogLevel::None },
|
||||||
|
}};
|
||||||
|
|
||||||
|
const std::string logLevelStr = env::getEnvVar("DXVK_LOG_LEVEL");
|
||||||
|
|
||||||
|
for (const auto& pair : logLevels) {
|
||||||
|
if (logLevelStr == pair.first)
|
||||||
|
return pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LogLevel::Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
src/util/log/log.h
Normal file
69
src/util/log/log.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../thread.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
enum class LogLevel : uint32_t {
|
||||||
|
Trace = 0,
|
||||||
|
Debug = 1,
|
||||||
|
Info = 2,
|
||||||
|
Warn = 3,
|
||||||
|
Error = 4,
|
||||||
|
None = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
using PFN_wineLogOutput = int (STDMETHODCALLTYPE *)(const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Logger
|
||||||
|
*
|
||||||
|
* Logger for one DLL. Creates a text file and
|
||||||
|
* writes all log messages to that file.
|
||||||
|
*/
|
||||||
|
class Logger {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Logger(const std::string& file_name);
|
||||||
|
~Logger();
|
||||||
|
|
||||||
|
static void trace(const std::string& message);
|
||||||
|
static void debug(const std::string& message);
|
||||||
|
static void info (const std::string& message);
|
||||||
|
static void warn (const std::string& message);
|
||||||
|
static void err (const std::string& message);
|
||||||
|
static void log (LogLevel level, const std::string& message);
|
||||||
|
|
||||||
|
static LogLevel logLevel() {
|
||||||
|
return s_instance.m_minLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static Logger s_instance;
|
||||||
|
|
||||||
|
const LogLevel m_minLevel;
|
||||||
|
const std::string m_fileName;
|
||||||
|
|
||||||
|
dxvk::mutex m_mutex;
|
||||||
|
std::ofstream m_fileStream;
|
||||||
|
|
||||||
|
bool m_initialized = false;
|
||||||
|
PFN_wineLogOutput m_wineLogOutput = nullptr;
|
||||||
|
|
||||||
|
void emitMsg(LogLevel level, const std::string& message);
|
||||||
|
|
||||||
|
std::string getFileName(
|
||||||
|
const std::string& base);
|
||||||
|
|
||||||
|
static LogLevel getMinLogLevel();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
11
src/util/log/log_debug.cpp
Normal file
11
src/util/log/log_debug.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include "log_debug.h"
|
||||||
|
|
||||||
|
namespace dxvk::debug {
|
||||||
|
|
||||||
|
std::string methodName(const std::string& prettyName) {
|
||||||
|
size_t end = prettyName.find("(");
|
||||||
|
size_t begin = prettyName.substr(0, end).rfind(" ") + 1;
|
||||||
|
return prettyName.substr(begin,end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
src/util/log/log_debug.h
Normal file
49
src/util/log/log_debug.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define METHOD_NAME __FUNCSIG__
|
||||||
|
#else
|
||||||
|
#define METHOD_NAME __PRETTY_FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TRACE_ENABLED
|
||||||
|
|
||||||
|
#ifdef TRACE_ENABLED
|
||||||
|
#define TRACE(...) \
|
||||||
|
do { dxvk::debug::trace(METHOD_NAME, ##__VA_ARGS__); } while (0)
|
||||||
|
#else
|
||||||
|
#define TRACE(...) \
|
||||||
|
do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace dxvk::debug {
|
||||||
|
|
||||||
|
std::string methodName(const std::string& prettyName);
|
||||||
|
|
||||||
|
inline void traceArgs(std::stringstream& stream) { }
|
||||||
|
|
||||||
|
template<typename Arg1>
|
||||||
|
void traceArgs(std::stringstream& stream, const Arg1& arg1) {
|
||||||
|
stream << arg1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename Arg2, typename... Args>
|
||||||
|
void traceArgs(std::stringstream& stream, const Arg1& arg1, const Arg2& arg2, const Args&... args) {
|
||||||
|
stream << arg1 << ",";
|
||||||
|
traceArgs(stream, arg2, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void trace(const std::string& funcName, const Args&... args) {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << methodName(funcName) << "(";
|
||||||
|
traceArgs(stream, args...);
|
||||||
|
stream << ")";
|
||||||
|
Logger::trace(stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/util/rc/util_rc.h
Normal file
38
src/util/rc/util_rc.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "../util_likely.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reference-counted object
|
||||||
|
*/
|
||||||
|
class RcObject {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Increments reference count
|
||||||
|
* \returns New reference count
|
||||||
|
*/
|
||||||
|
force_inline uint32_t incRef() {
|
||||||
|
return ++m_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decrements reference count
|
||||||
|
* \returns New reference count
|
||||||
|
*/
|
||||||
|
force_inline uint32_t decRef() {
|
||||||
|
return --m_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::atomic<uint32_t> m_refCount = { 0u };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
124
src/util/rc/util_rc_ptr.h
Normal file
124
src/util/rc/util_rc_ptr.h
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
force_inline void incRef() const {
|
||||||
|
if (m_object != nullptr)
|
||||||
|
m_object->incRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
force_inline void decRef() const {
|
||||||
|
if (m_object != nullptr) {
|
||||||
|
if (m_object->decRef() == 0)
|
||||||
|
delete m_object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::ostream& operator << (std::ostream& os, const dxvk::Rc<T>& rc) {
|
||||||
|
return os << rc.ptr();
|
||||||
|
}
|
114
src/util/thread.cpp
Normal file
114
src/util/thread.cpp
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "thread.h"
|
||||||
|
#include "util_likely.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
thread::thread(ThreadProc&& proc)
|
||||||
|
: m_data(new ThreadData(std::move(proc))) {
|
||||||
|
m_data->handle = ::CreateThread(nullptr, 0x100000,
|
||||||
|
thread::threadProc, m_data, STACK_SIZE_PARAM_IS_A_RESERVATION,
|
||||||
|
&m_data->id);
|
||||||
|
|
||||||
|
if (!m_data->handle) {
|
||||||
|
delete m_data;
|
||||||
|
throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again), "Failed to create thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
thread::~thread() {
|
||||||
|
if (joinable())
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void thread::join() {
|
||||||
|
if (!joinable())
|
||||||
|
throw std::system_error(std::make_error_code(std::errc::invalid_argument), "Thread not joinable");
|
||||||
|
|
||||||
|
if (get_id() == this_thread::get_id())
|
||||||
|
throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur), "Cannot join current thread");
|
||||||
|
|
||||||
|
if(::WaitForSingleObjectEx(m_data->handle, INFINITE, FALSE) == WAIT_FAILED)
|
||||||
|
throw std::system_error(std::make_error_code(std::errc::invalid_argument), "Joining thread failed");
|
||||||
|
|
||||||
|
detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void thread::set_priority(ThreadPriority priority) {
|
||||||
|
int32_t value;
|
||||||
|
switch (priority) {
|
||||||
|
default:
|
||||||
|
case ThreadPriority::Normal: value = THREAD_PRIORITY_NORMAL; break;
|
||||||
|
case ThreadPriority::Lowest: value = THREAD_PRIORITY_LOWEST; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data)
|
||||||
|
::SetThreadPriority(m_data->handle, int32_t(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t thread::hardware_concurrency() {
|
||||||
|
SYSTEM_INFO info = { };
|
||||||
|
::GetSystemInfo(&info);
|
||||||
|
return info.dwNumberOfProcessors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DWORD WINAPI thread::threadProc(void* arg) {
|
||||||
|
auto data = reinterpret_cast<ThreadData*>(arg);
|
||||||
|
DWORD exitCode = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data->proc();
|
||||||
|
} catch (...) {
|
||||||
|
exitCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->decRef();
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace dxvk::this_thread {
|
||||||
|
|
||||||
|
bool isInModuleDetachment() {
|
||||||
|
using PFN_RtlDllShutdownInProgress = BOOLEAN (WINAPI *)();
|
||||||
|
|
||||||
|
static auto RtlDllShutdownInProgress = reinterpret_cast<PFN_RtlDllShutdownInProgress>(
|
||||||
|
::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "RtlDllShutdownInProgress"));
|
||||||
|
|
||||||
|
return RtlDllShutdownInProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace dxvk::this_thread {
|
||||||
|
|
||||||
|
static std::atomic<uint32_t> g_threadCtr = { 0u };
|
||||||
|
static thread_local uint32_t g_threadId = 0u;
|
||||||
|
|
||||||
|
// This implementation returns thread ids unique to the current instance.
|
||||||
|
// ie. if you use this across multiple .so's then you might get conflicting ids.
|
||||||
|
//
|
||||||
|
// This isn't an issue for us, as it is only used by the spinlock implementation,
|
||||||
|
// but may be for you if you use this elsewhere.
|
||||||
|
uint32_t get_id() {
|
||||||
|
if (unlikely(!g_threadId))
|
||||||
|
g_threadId = ++g_threadCtr;
|
||||||
|
|
||||||
|
return g_threadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
342
src/util/thread.h
Normal file
342
src/util/thread.h
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "util_error.h"
|
||||||
|
|
||||||
|
#include "./com/com_include.h"
|
||||||
|
|
||||||
|
#include "./rc/util_rc.h"
|
||||||
|
#include "./rc/util_rc_ptr.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Thread priority
|
||||||
|
*/
|
||||||
|
enum class ThreadPriority : int32_t {
|
||||||
|
Normal,
|
||||||
|
Lowest,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
using ThreadProc = std::function<void()>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Thread object
|
||||||
|
*/
|
||||||
|
struct ThreadData {
|
||||||
|
ThreadData(ThreadProc&& proc_)
|
||||||
|
: proc(std::move(proc_)) { }
|
||||||
|
|
||||||
|
~ThreadData() {
|
||||||
|
if (handle)
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE handle = nullptr;
|
||||||
|
DWORD id = 0;
|
||||||
|
std::atomic<uint32_t> refs = { 2u };
|
||||||
|
ThreadProc proc;
|
||||||
|
|
||||||
|
void decRef() {
|
||||||
|
if (refs.fetch_sub(1, std::memory_order_release) == 1)
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Thread wrapper
|
||||||
|
*
|
||||||
|
* Drop-in replacement for std::thread
|
||||||
|
* using plain win32 threads.
|
||||||
|
*/
|
||||||
|
class thread {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using id = uint32_t;
|
||||||
|
using native_handle_type = HANDLE;
|
||||||
|
|
||||||
|
thread() { }
|
||||||
|
|
||||||
|
explicit thread(ThreadProc&& proc);
|
||||||
|
|
||||||
|
~thread();
|
||||||
|
|
||||||
|
thread(thread&& other)
|
||||||
|
: m_data(std::exchange(other.m_data, nullptr)) { }
|
||||||
|
|
||||||
|
thread& operator = (thread&& other) {
|
||||||
|
if (m_data)
|
||||||
|
m_data->decRef();
|
||||||
|
|
||||||
|
m_data = std::exchange(other.m_data, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void detach() {
|
||||||
|
m_data->decRef();
|
||||||
|
m_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool joinable() const {
|
||||||
|
return m_data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
id get_id() const {
|
||||||
|
return joinable() ? m_data->id : id();
|
||||||
|
}
|
||||||
|
|
||||||
|
native_handle_type native_handle() const {
|
||||||
|
return joinable() ? m_data->handle : native_handle_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(thread& other) {
|
||||||
|
std::swap(m_data, other.m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void join();
|
||||||
|
|
||||||
|
void set_priority(ThreadPriority priority);
|
||||||
|
|
||||||
|
static uint32_t hardware_concurrency();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ThreadData* m_data = nullptr;
|
||||||
|
|
||||||
|
static DWORD WINAPI threadProc(void* arg);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace this_thread {
|
||||||
|
inline void yield() {
|
||||||
|
SwitchToThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline thread::id get_id() {
|
||||||
|
return thread::id(GetCurrentThreadId());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInModuleDetachment();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SRW-based mutex implementation
|
||||||
|
*
|
||||||
|
* Drop-in replacement for \c std::mutex that uses Win32
|
||||||
|
* SRW locks, which are implemented with \c futex in wine.
|
||||||
|
*/
|
||||||
|
class mutex {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using native_handle_type = PSRWLOCK;
|
||||||
|
|
||||||
|
mutex() { }
|
||||||
|
|
||||||
|
mutex(const mutex&) = delete;
|
||||||
|
mutex& operator = (const mutex&) = delete;
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
AcquireSRWLockExclusive(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
ReleaseSRWLockExclusive(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock() {
|
||||||
|
return TryAcquireSRWLockExclusive(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
native_handle_type native_handle() {
|
||||||
|
return &m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
SRWLOCK m_lock = SRWLOCK_INIT;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Recursive mutex implementation
|
||||||
|
*
|
||||||
|
* Drop-in replacement for \c std::recursive_mutex that
|
||||||
|
* uses Win32 critical sections.
|
||||||
|
*/
|
||||||
|
class recursive_mutex {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using native_handle_type = PCRITICAL_SECTION;
|
||||||
|
|
||||||
|
recursive_mutex() {
|
||||||
|
InitializeCriticalSection(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~recursive_mutex() {
|
||||||
|
DeleteCriticalSection(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive_mutex(const recursive_mutex&) = delete;
|
||||||
|
recursive_mutex& operator = (const recursive_mutex&) = delete;
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
EnterCriticalSection(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
LeaveCriticalSection(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock() {
|
||||||
|
return TryEnterCriticalSection(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
native_handle_type native_handle() {
|
||||||
|
return &m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
CRITICAL_SECTION m_lock;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SRW-based condition variable implementation
|
||||||
|
*
|
||||||
|
* Drop-in replacement for \c std::condition_variable that
|
||||||
|
* uses Win32 condition variables on SRW locks.
|
||||||
|
*/
|
||||||
|
class condition_variable {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using native_handle_type = PCONDITION_VARIABLE;
|
||||||
|
|
||||||
|
condition_variable() {
|
||||||
|
InitializeConditionVariable(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
condition_variable(condition_variable&) = delete;
|
||||||
|
|
||||||
|
condition_variable& operator = (condition_variable&) = delete;
|
||||||
|
|
||||||
|
void notify_one() {
|
||||||
|
WakeConditionVariable(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify_all() {
|
||||||
|
WakeAllConditionVariable(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait(std::unique_lock<dxvk::mutex>& lock) {
|
||||||
|
auto srw = lock.mutex()->native_handle();
|
||||||
|
SleepConditionVariableSRW(&m_cond, srw, INFINITE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Predicate>
|
||||||
|
void wait(std::unique_lock<dxvk::mutex>& lock, Predicate pred) {
|
||||||
|
while (!pred())
|
||||||
|
wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Clock, typename Duration>
|
||||||
|
std::cv_status wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time) {
|
||||||
|
auto now = Clock::now();
|
||||||
|
|
||||||
|
return (now < time)
|
||||||
|
? wait_for(lock, now - time)
|
||||||
|
: std::cv_status::timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Clock, typename Duration, typename Predicate>
|
||||||
|
bool wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time, Predicate pred) {
|
||||||
|
if (pred())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto now = Clock::now();
|
||||||
|
return now < time && wait_for(lock, now - time, pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Rep, typename Period>
|
||||||
|
std::cv_status wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout) {
|
||||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
|
||||||
|
auto srw = lock.mutex()->native_handle();
|
||||||
|
|
||||||
|
return SleepConditionVariableSRW(&m_cond, srw, ms.count(), 0)
|
||||||
|
? std::cv_status::no_timeout
|
||||||
|
: std::cv_status::timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Rep, typename Period, typename Predicate>
|
||||||
|
bool wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout, Predicate pred) {
|
||||||
|
bool result = pred();
|
||||||
|
|
||||||
|
if (!result && wait_for(lock, timeout) == std::cv_status::no_timeout)
|
||||||
|
result = pred();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
native_handle_type native_handle() {
|
||||||
|
return &m_cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
CONDITION_VARIABLE m_cond;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
class thread : public std::thread {
|
||||||
|
public:
|
||||||
|
using std::thread::thread;
|
||||||
|
|
||||||
|
void set_priority(ThreadPriority priority) {
|
||||||
|
::sched_param param = {};
|
||||||
|
int32_t policy;
|
||||||
|
switch (priority) {
|
||||||
|
default:
|
||||||
|
case ThreadPriority::Normal: policy = SCHED_OTHER; break;
|
||||||
|
case ThreadPriority::Lowest: policy = SCHED_IDLE; break;
|
||||||
|
}
|
||||||
|
::pthread_setschedparam(this->native_handle(), policy, ¶m);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using mutex = std::mutex;
|
||||||
|
using recursive_mutex = std::recursive_mutex;
|
||||||
|
using condition_variable = std::condition_variable;
|
||||||
|
|
||||||
|
namespace this_thread {
|
||||||
|
inline void yield() {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_id();
|
||||||
|
|
||||||
|
inline bool isInModuleDetachment() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
519
src/util/util_bit.h
Normal file
519
src/util/util_bit.h
Normal file
|
@ -0,0 +1,519 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
|
||||||
|
#define DXVK_ARCH_X86
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64)
|
||||||
|
#define DXVK_ARCH_X86_64
|
||||||
|
#endif
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
#define DXVK_ARCH_ARM64
|
||||||
|
#else
|
||||||
|
#error "Unknown CPU Architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DXVK_ARCH_X86
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#if defined(__WINE__) && defined(__clang__)
|
||||||
|
#pragma push_macro("_WIN32")
|
||||||
|
#undef _WIN32
|
||||||
|
#endif
|
||||||
|
#include <x86intrin.h>
|
||||||
|
#if defined(__WINE__) && defined(__clang__)
|
||||||
|
#pragma pop_macro("_WIN32")
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "util_likely.h"
|
||||||
|
#include "util_math.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dxvk::bit {
|
||||||
|
|
||||||
|
template<typename T, typename J>
|
||||||
|
T cast(const J& src) {
|
||||||
|
static_assert(sizeof(T) == sizeof(J));
|
||||||
|
static_assert(std::is_trivially_copyable<J>::value && std::is_trivial<T>::value);
|
||||||
|
|
||||||
|
T dst;
|
||||||
|
std::memcpy(&dst, &src, sizeof(T));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T extract(T value, uint32_t fst, uint32_t lst) {
|
||||||
|
return (value >> fst) & ~(~T(0) << (lst - fst + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t popcntStep(uint32_t n, uint32_t mask, uint32_t shift) {
|
||||||
|
return (n & mask) + ((n & ~mask) >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t popcnt(uint32_t n) {
|
||||||
|
n = popcntStep(n, 0x55555555, 1);
|
||||||
|
n = popcntStep(n, 0x33333333, 2);
|
||||||
|
n = popcntStep(n, 0x0F0F0F0F, 4);
|
||||||
|
n = popcntStep(n, 0x00FF00FF, 8);
|
||||||
|
n = popcntStep(n, 0x0000FFFF, 16);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t tzcnt(uint32_t n) {
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
return _tzcnt_u32(n);
|
||||||
|
#elif defined(__BMI__)
|
||||||
|
return __tzcnt_u32(n);
|
||||||
|
#elif defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__))
|
||||||
|
// tzcnt is encoded as rep bsf, so we can use it on all
|
||||||
|
// processors, but the behaviour of zero inputs differs:
|
||||||
|
// - bsf: zf = 1, cf = ?, result = ?
|
||||||
|
// - tzcnt: zf = 0, cf = 1, result = 32
|
||||||
|
// We'll have to handle this case manually.
|
||||||
|
uint32_t res;
|
||||||
|
uint32_t tmp;
|
||||||
|
asm (
|
||||||
|
"tzcnt %2, %0;"
|
||||||
|
"mov $32, %1;"
|
||||||
|
"test %2, %2;"
|
||||||
|
"cmovz %1, %0;"
|
||||||
|
: "=&r" (res), "=&r" (tmp)
|
||||||
|
: "r" (n)
|
||||||
|
: "cc");
|
||||||
|
return res;
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
return n != 0 ? __builtin_ctz(n) : 32;
|
||||||
|
#else
|
||||||
|
uint32_t r = 31;
|
||||||
|
n &= -n;
|
||||||
|
r -= (n & 0x0000FFFF) ? 16 : 0;
|
||||||
|
r -= (n & 0x00FF00FF) ? 8 : 0;
|
||||||
|
r -= (n & 0x0F0F0F0F) ? 4 : 0;
|
||||||
|
r -= (n & 0x33333333) ? 2 : 0;
|
||||||
|
r -= (n & 0x55555555) ? 1 : 0;
|
||||||
|
return n != 0 ? r : 32;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t tzcnt(uint64_t n) {
|
||||||
|
#if defined(DXVK_ARCH_X86_64) && defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
return (uint32_t)_tzcnt_u64(n);
|
||||||
|
#elif defined(DXVK_ARCH_X86_64) && defined(__BMI__)
|
||||||
|
return __tzcnt_u64(n);
|
||||||
|
#elif defined(DXVK_ARCH_X86_64) && (defined(__GNUC__) || defined(__clang__))
|
||||||
|
uint64_t res;
|
||||||
|
uint64_t tmp;
|
||||||
|
asm (
|
||||||
|
"tzcnt %2, %0;"
|
||||||
|
"mov $64, %1;"
|
||||||
|
"test %2, %2;"
|
||||||
|
"cmovz %1, %0;"
|
||||||
|
: "=&r" (res), "=&r" (tmp)
|
||||||
|
: "r" (n)
|
||||||
|
: "cc");
|
||||||
|
return res;
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
return n != 0 ? __builtin_ctzll(n) : 64;
|
||||||
|
#else
|
||||||
|
uint32_t lo = uint32_t(n);
|
||||||
|
if (lo) {
|
||||||
|
return tzcnt(lo);
|
||||||
|
} else {
|
||||||
|
uint32_t hi = uint32_t(n >> 32);
|
||||||
|
return tzcnt(hi) + 32;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t lzcnt(uint32_t n) {
|
||||||
|
#if (defined(_MSC_VER) && !defined(__clang__)) || defined(__LZCNT__)
|
||||||
|
return _lzcnt_u32(n);
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
return n != 0 ? __builtin_clz(n) : 32;
|
||||||
|
#else
|
||||||
|
uint32_t r = 0;
|
||||||
|
|
||||||
|
if (n == 0) return 32;
|
||||||
|
|
||||||
|
if (n <= 0x0000FFFF) { r += 16; n <<= 16; }
|
||||||
|
if (n <= 0x00FFFFFF) { r += 8; n <<= 8; }
|
||||||
|
if (n <= 0x0FFFFFFF) { r += 4; n <<= 4; }
|
||||||
|
if (n <= 0x3FFFFFFF) { r += 2; n <<= 2; }
|
||||||
|
if (n <= 0x7FFFFFFF) { r += 1; n <<= 1; }
|
||||||
|
|
||||||
|
return r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
uint32_t pack(T& dst, uint32_t& shift, T src, uint32_t count) {
|
||||||
|
constexpr uint32_t Bits = 8 * sizeof(T);
|
||||||
|
if (likely(shift < Bits))
|
||||||
|
dst |= src << shift;
|
||||||
|
shift += count;
|
||||||
|
return shift > Bits ? shift - Bits : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
uint32_t unpack(T& dst, T src, uint32_t& shift, uint32_t count) {
|
||||||
|
constexpr uint32_t Bits = 8 * sizeof(T);
|
||||||
|
if (likely(shift < Bits))
|
||||||
|
dst = (src >> shift) & ((T(1) << count) - 1);
|
||||||
|
shift += count;
|
||||||
|
return shift > Bits ? shift - Bits : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compares two aligned structs bit by bit
|
||||||
|
*
|
||||||
|
* \param [in] a First struct
|
||||||
|
* \param [in] b Second struct
|
||||||
|
* \returns \c true if the structs are equal
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
bool bcmpeq(const T* a, const T* b) {
|
||||||
|
static_assert(alignof(T) >= 16);
|
||||||
|
#if defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))
|
||||||
|
auto ai = reinterpret_cast<const __m128i*>(a);
|
||||||
|
auto bi = reinterpret_cast<const __m128i*>(b);
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma nounroll
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC unroll 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for ( ; i < 2 * (sizeof(T) / 32); i += 2) {
|
||||||
|
__m128i eq0 = _mm_cmpeq_epi8(
|
||||||
|
_mm_load_si128(ai + i),
|
||||||
|
_mm_load_si128(bi + i));
|
||||||
|
__m128i eq1 = _mm_cmpeq_epi8(
|
||||||
|
_mm_load_si128(ai + i + 1),
|
||||||
|
_mm_load_si128(bi + i + 1));
|
||||||
|
__m128i eq = _mm_and_si128(eq0, eq1);
|
||||||
|
|
||||||
|
int mask = _mm_movemask_epi8(eq);
|
||||||
|
if (mask != 0xFFFF)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; i < sizeof(T) / 16; i++) {
|
||||||
|
__m128i eq = _mm_cmpeq_epi8(
|
||||||
|
_mm_load_si128(ai + i),
|
||||||
|
_mm_load_si128(bi + i));
|
||||||
|
|
||||||
|
int mask = _mm_movemask_epi8(eq);
|
||||||
|
if (mask != 0xFFFF)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return !std::memcmp(a, b, sizeof(T));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Bits>
|
||||||
|
class bitset {
|
||||||
|
static constexpr size_t Dwords = align(Bits, 32) / 32;
|
||||||
|
public:
|
||||||
|
|
||||||
|
constexpr bitset()
|
||||||
|
: m_dwords() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool get(uint32_t idx) const {
|
||||||
|
uint32_t dword = 0;
|
||||||
|
uint32_t bit = idx;
|
||||||
|
|
||||||
|
// Compiler doesn't remove this otherwise.
|
||||||
|
if constexpr (Dwords > 1) {
|
||||||
|
dword = idx / 32;
|
||||||
|
bit = idx % 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_dwords[dword] & (1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void set(uint32_t idx, bool value) {
|
||||||
|
uint32_t dword = 0;
|
||||||
|
uint32_t bit = idx;
|
||||||
|
|
||||||
|
// Compiler doesn't remove this otherwise.
|
||||||
|
if constexpr (Dwords > 1) {
|
||||||
|
dword = idx / 32;
|
||||||
|
bit = idx % 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
m_dwords[dword] |= 1u << bit;
|
||||||
|
else
|
||||||
|
m_dwords[dword] &= ~(1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool exchange(uint32_t idx, bool value) {
|
||||||
|
bool oldValue = get(idx);
|
||||||
|
set(idx, value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void flip(uint32_t idx) {
|
||||||
|
uint32_t dword = 0;
|
||||||
|
uint32_t bit = idx;
|
||||||
|
|
||||||
|
// Compiler doesn't remove this otherwise.
|
||||||
|
if constexpr (Dwords > 1) {
|
||||||
|
dword = idx / 32;
|
||||||
|
bit = idx % 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dwords[dword] ^= 1u << bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void setAll() {
|
||||||
|
if constexpr (Bits % 32 == 0) {
|
||||||
|
for (size_t i = 0; i < Dwords; i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t i = 0; i < Dwords - 1; i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
m_dwords[Dwords - 1] = (1u << (Bits % 32)) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void clearAll() {
|
||||||
|
for (size_t i = 0; i < Dwords; i++)
|
||||||
|
m_dwords[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool any() const {
|
||||||
|
for (size_t i = 0; i < Dwords; i++) {
|
||||||
|
if (m_dwords[i] != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t& dword(uint32_t idx) {
|
||||||
|
return m_dwords[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t bitCount() {
|
||||||
|
return Bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t dwordCount() {
|
||||||
|
return Dwords;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator [] (uint32_t idx) const {
|
||||||
|
return get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void setN(uint32_t bits) {
|
||||||
|
uint32_t fullDwords = bits / 32;
|
||||||
|
uint32_t offset = bits % 32;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fullDwords; i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
if (offset > 0)
|
||||||
|
m_dwords[fullDwords] = (1u << offset) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t m_dwords[Dwords];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class bitvector {
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool get(uint32_t idx) const {
|
||||||
|
uint32_t dword = idx / 32;
|
||||||
|
uint32_t bit = idx % 32;
|
||||||
|
|
||||||
|
return m_dwords[dword] & (1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureSize(uint32_t bitCount) {
|
||||||
|
uint32_t dword = bitCount / 32;
|
||||||
|
if (unlikely(dword >= m_dwords.size())) {
|
||||||
|
m_dwords.resize(dword + 1);
|
||||||
|
}
|
||||||
|
m_bitCount = std::max(m_bitCount, bitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(uint32_t idx, bool value) {
|
||||||
|
ensureSize(idx + 1);
|
||||||
|
|
||||||
|
uint32_t dword = 0;
|
||||||
|
uint32_t bit = idx;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
m_dwords[dword] |= 1u << bit;
|
||||||
|
else
|
||||||
|
m_dwords[dword] &= ~(1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exchange(uint32_t idx, bool value) {
|
||||||
|
ensureSize(idx + 1);
|
||||||
|
|
||||||
|
bool oldValue = get(idx);
|
||||||
|
set(idx, value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flip(uint32_t idx) {
|
||||||
|
ensureSize(idx + 1);
|
||||||
|
|
||||||
|
uint32_t dword = idx / 32;
|
||||||
|
uint32_t bit = idx % 32;
|
||||||
|
|
||||||
|
m_dwords[dword] ^= 1u << bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAll() {
|
||||||
|
if (m_bitCount % 32 == 0) {
|
||||||
|
for (size_t i = 0; i < m_dwords.size(); i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t i = 0; i < m_dwords.size() - 1; i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
m_dwords[m_dwords.size() - 1] = (1u << (m_bitCount % 32)) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearAll() {
|
||||||
|
for (size_t i = 0; i < m_dwords.size(); i++)
|
||||||
|
m_dwords[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool any() const {
|
||||||
|
for (size_t i = 0; i < m_dwords.size(); i++) {
|
||||||
|
if (m_dwords[i] != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t& dword(uint32_t idx) {
|
||||||
|
return m_dwords[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bitCount() const {
|
||||||
|
return m_bitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dwordCount() const {
|
||||||
|
return m_dwords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator [] (uint32_t idx) const {
|
||||||
|
return get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setN(uint32_t bits) {
|
||||||
|
ensureSize(bits);
|
||||||
|
|
||||||
|
uint32_t fullDwords = bits / 32;
|
||||||
|
uint32_t offset = bits % 32;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fullDwords; i++)
|
||||||
|
m_dwords[i] = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
if (offset > 0)
|
||||||
|
m_dwords[fullDwords] = (1u << offset) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_dwords;
|
||||||
|
uint32_t m_bitCount = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitMask {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
public:
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using value_type = uint32_t;
|
||||||
|
using difference_type = uint32_t;
|
||||||
|
using pointer = const uint32_t*;
|
||||||
|
using reference = uint32_t;
|
||||||
|
|
||||||
|
explicit iterator(uint32_t flags)
|
||||||
|
: m_mask(flags) { }
|
||||||
|
|
||||||
|
iterator& operator ++ () {
|
||||||
|
m_mask &= m_mask - 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator ++ (int) {
|
||||||
|
iterator retval = *this;
|
||||||
|
m_mask &= m_mask - 1;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t operator * () const {
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__BMI__) && defined(DXVK_ARCH_X86)
|
||||||
|
uint32_t res;
|
||||||
|
asm ("tzcnt %1,%0"
|
||||||
|
: "=r" (res)
|
||||||
|
: "r" (m_mask)
|
||||||
|
: "cc");
|
||||||
|
return res;
|
||||||
|
#else
|
||||||
|
return tzcnt(m_mask);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (iterator other) const { return m_mask == other.m_mask; }
|
||||||
|
bool operator != (iterator other) const { return m_mask != other.m_mask; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t m_mask;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
BitMask()
|
||||||
|
: m_mask(0) { }
|
||||||
|
|
||||||
|
BitMask(uint32_t n)
|
||||||
|
: m_mask(n) { }
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
return iterator(m_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return iterator(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
uint32_t m_mask;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
7
src/util/util_enum.h
Normal file
7
src/util/util_enum.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define ENUM_NAME(name) \
|
||||||
|
case name: return os << #name
|
||||||
|
|
||||||
|
#define ENUM_DEFAULT(name) \
|
||||||
|
default: return os << static_cast<int32_t>(e)
|
131
src/util/util_env.cpp
Normal file
131
src/util/util_env.cpp
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "util_env.h"
|
||||||
|
|
||||||
|
#include "./com/com_include.h"
|
||||||
|
|
||||||
|
namespace dxvk::env {
|
||||||
|
|
||||||
|
std::string getEnvVar(const char* name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::vector<WCHAR> result;
|
||||||
|
result.resize(MAX_PATH + 1);
|
||||||
|
|
||||||
|
DWORD len = ::GetEnvironmentVariableW(str::tows(name).c_str(), result.data(), MAX_PATH);
|
||||||
|
result.resize(len);
|
||||||
|
|
||||||
|
return str::fromws(result.data());
|
||||||
|
#else
|
||||||
|
const char* result = std::getenv(name);
|
||||||
|
return result ? result : "";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t matchFileExtension(const std::string& name, const char* ext) {
|
||||||
|
auto pos = name.find_last_of('.');
|
||||||
|
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
bool matches = std::accumulate(name.begin() + pos + 1, name.end(), true,
|
||||||
|
[&ext] (bool current, char a) {
|
||||||
|
if (a >= 'A' && a <= 'Z')
|
||||||
|
a += 'a' - 'A';
|
||||||
|
return current && *ext && a == *(ext++);
|
||||||
|
});
|
||||||
|
|
||||||
|
return matches ? pos : std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string getExeName() {
|
||||||
|
std::string fullPath = getExePath();
|
||||||
|
auto n = fullPath.find_last_of(env::PlatformDirSlash);
|
||||||
|
|
||||||
|
return (n != std::string::npos)
|
||||||
|
? fullPath.substr(n + 1)
|
||||||
|
: fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string getExeBaseName() {
|
||||||
|
auto exeName = getExeName();
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto extp = matchFileExtension(exeName, "exe");
|
||||||
|
|
||||||
|
if (extp != std::string::npos)
|
||||||
|
exeName.erase(extp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return exeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string getExePath() {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
std::vector<WCHAR> exePath;
|
||||||
|
exePath.resize(MAX_PATH + 1);
|
||||||
|
|
||||||
|
DWORD len = ::GetModuleFileNameW(NULL, exePath.data(), MAX_PATH);
|
||||||
|
exePath.resize(len);
|
||||||
|
|
||||||
|
return str::fromws(exePath.data());
|
||||||
|
#elif defined(__linux__)
|
||||||
|
std::array<char, PATH_MAX> exePath = {};
|
||||||
|
|
||||||
|
size_t count = readlink("/proc/self/exe", exePath.data(), exePath.size());
|
||||||
|
|
||||||
|
return std::string(exePath.begin(), exePath.begin() + count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setThreadName(const std::string& name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
using SetThreadDescriptionProc = HRESULT (WINAPI *) (HANDLE, PCWSTR);
|
||||||
|
|
||||||
|
static auto SetThreadDescription = reinterpret_cast<SetThreadDescriptionProc>(
|
||||||
|
::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "SetThreadDescription"));
|
||||||
|
|
||||||
|
if (SetThreadDescription) {
|
||||||
|
std::array<wchar_t, 16> wideName = { };
|
||||||
|
|
||||||
|
str::transcodeString(
|
||||||
|
wideName.data(), wideName.size() - 1,
|
||||||
|
name.data(), name.size());
|
||||||
|
|
||||||
|
SetThreadDescription(::GetCurrentThread(), wideName.data());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::array<char, 16> posixName = {};
|
||||||
|
dxvk::str::strlcpy(posixName.data(), name.c_str(), 16);
|
||||||
|
::pthread_setname_np(pthread_self(), posixName.data());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool createDirectory(const std::string& path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::array<WCHAR, MAX_PATH + 1> widePath;
|
||||||
|
|
||||||
|
size_t length = str::transcodeString(
|
||||||
|
widePath.data(), widePath.size() - 1,
|
||||||
|
path.data(), path.size());
|
||||||
|
|
||||||
|
widePath[length] = L'\0';
|
||||||
|
return !!CreateDirectoryW(widePath.data(), nullptr);
|
||||||
|
#else
|
||||||
|
return std::filesystem::create_directories(path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
src/util/util_env.h
Normal file
79
src/util/util_env.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util_string.h"
|
||||||
|
|
||||||
|
namespace dxvk::env {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
constexpr char PlatformDirSlash = '\\';
|
||||||
|
#else
|
||||||
|
constexpr char PlatformDirSlash = '/';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether the host platform is 32-bit
|
||||||
|
*/
|
||||||
|
constexpr bool is32BitHostPlatform() {
|
||||||
|
return sizeof(void*) == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets environment variable
|
||||||
|
*
|
||||||
|
* If the variable is not defined, this will return
|
||||||
|
* an empty string. Note that environment variables
|
||||||
|
* may be defined with an empty value.
|
||||||
|
* \param [in] name Name of the variable
|
||||||
|
* \returns Value of the variable
|
||||||
|
*/
|
||||||
|
std::string getEnvVar(const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether a file name has a given extension
|
||||||
|
*
|
||||||
|
* \param [in] name File name
|
||||||
|
* \param [in] ext Extension to match, in lowercase letters
|
||||||
|
* \returns Position of the extension within the file name, or
|
||||||
|
* \c std::string::npos if the file has a different extension
|
||||||
|
*/
|
||||||
|
size_t matchFileExtension(const std::string& name, const char* ext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets the executable name
|
||||||
|
*
|
||||||
|
* Returns the base name (not the full path) of the
|
||||||
|
* program executable, including the file extension.
|
||||||
|
* This function should be used to identify programs.
|
||||||
|
* \returns Executable name
|
||||||
|
*/
|
||||||
|
std::string getExeName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets the executable name without extension
|
||||||
|
*
|
||||||
|
* Same as \ref getExeName but without the file extension.
|
||||||
|
* \returns Executable name
|
||||||
|
*/
|
||||||
|
std::string getExeBaseName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets full path to executable
|
||||||
|
* \returns Path to executable
|
||||||
|
*/
|
||||||
|
std::string getExePath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets name of the calling thread
|
||||||
|
* \param [in] name Thread name
|
||||||
|
*/
|
||||||
|
void setThreadName(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates a directory
|
||||||
|
*
|
||||||
|
* \param [in] path Path to directory
|
||||||
|
* \returns \c true on success
|
||||||
|
*/
|
||||||
|
bool createDirectory(const std::string& path);
|
||||||
|
|
||||||
|
}
|
31
src/util/util_error.h
Normal file
31
src/util/util_error.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
110
src/util/util_flags.h
Normal file
110
src/util/util_flags.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "util_bit.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Flags {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using IntType = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
Flags() { }
|
||||||
|
|
||||||
|
Flags(IntType t)
|
||||||
|
: m_bits(t) { }
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
Flags(T f, Tx... fx) {
|
||||||
|
this->set(f, fx...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
void set(Tx... fx) {
|
||||||
|
m_bits |= bits(fx...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(Flags flags) {
|
||||||
|
m_bits |= flags.m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
void clr(Tx... fx) {
|
||||||
|
m_bits &= ~bits(fx...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clr(Flags flags) {
|
||||||
|
m_bits &= ~flags.m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
bool any(Tx... fx) const {
|
||||||
|
return (m_bits & bits(fx...)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
bool all(Tx... fx) const {
|
||||||
|
const IntType mask = bits(fx...);
|
||||||
|
return (m_bits & mask) == mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test(T f) const {
|
||||||
|
return this->any(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isClear() const {
|
||||||
|
return m_bits == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clrAll() {
|
||||||
|
m_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntType raw() const {
|
||||||
|
return m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flags operator & (const Flags& other) const {
|
||||||
|
return Flags(m_bits & other.m_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
Flags operator | (const Flags& other) const {
|
||||||
|
return Flags(m_bits | other.m_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
Flags operator ^ (const Flags& other) const {
|
||||||
|
return Flags(m_bits ^ other.m_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const Flags& other) const {
|
||||||
|
return m_bits == other.m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const Flags& other) const {
|
||||||
|
return m_bits != other.m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
IntType m_bits = 0;
|
||||||
|
|
||||||
|
static IntType bit(T f) {
|
||||||
|
return IntType(1) << static_cast<IntType>(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
static IntType bits(T f, Tx... fx) {
|
||||||
|
return bit(f) | bits(fx...);
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntType bits() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
11
src/util/util_likely.h
Normal file
11
src/util/util_likely.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define likely(x) __builtin_expect((x),1)
|
||||||
|
#define unlikely(x) __builtin_expect((x),0)
|
||||||
|
#define force_inline inline __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define likely(x) (x)
|
||||||
|
#define unlikely(x) (x)
|
||||||
|
#define force_inline inline
|
||||||
|
#endif
|
39
src/util/util_math.h
Normal file
39
src/util/util_math.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
constexpr size_t CACHE_LINE_SIZE = 64;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T clamp(T n, T lo, T hi) {
|
||||||
|
if (n < lo) return lo;
|
||||||
|
if (n > hi) return hi;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U = T>
|
||||||
|
constexpr T align(T what, U to) {
|
||||||
|
return (what + to - 1) & ~(to - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U = T>
|
||||||
|
constexpr T alignDown(T what, U to) {
|
||||||
|
return (what / to) * to;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent of std::clamp for use with floating point numbers
|
||||||
|
// Handles (-){INFINITY,NAN} cases.
|
||||||
|
// Will return min in cases of NAN, etc.
|
||||||
|
inline float fclamp(float value, float min, float max) {
|
||||||
|
return std::fmin(
|
||||||
|
std::fmax(value, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T divCeil(T dividend, T divisor) {
|
||||||
|
return (dividend + divisor - 1) / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
234
src/util/util_string.cpp
Normal file
234
src/util/util_string.cpp
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
#include "util_string.h"
|
||||||
|
|
||||||
|
namespace dxvk::str {
|
||||||
|
|
||||||
|
const uint8_t* decodeTypedChar(
|
||||||
|
const uint8_t* begin,
|
||||||
|
const uint8_t* end,
|
||||||
|
uint32_t& ch) {
|
||||||
|
uint32_t first = begin[0];
|
||||||
|
|
||||||
|
if (likely(first < 0x80)) {
|
||||||
|
// Basic ASCII character
|
||||||
|
ch = uint32_t(first);
|
||||||
|
return begin + 1;
|
||||||
|
} else if (unlikely(first < 0xC0)) {
|
||||||
|
// Character starts with a continuation byte,
|
||||||
|
// just skip until we find the next valid prefix
|
||||||
|
while ((begin < end) && (((*begin) & 0xC0) == 0x80))
|
||||||
|
begin += 1;
|
||||||
|
|
||||||
|
ch = uint32_t('?');
|
||||||
|
return begin;
|
||||||
|
} else {
|
||||||
|
// The number of leading 1 bits in the first byte
|
||||||
|
// determines the length of this character
|
||||||
|
size_t length = bit::lzcnt((~first) << 24);
|
||||||
|
|
||||||
|
if (unlikely(begin + length > end)) {
|
||||||
|
ch = uint32_t('?');
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first < 0xE0) {
|
||||||
|
ch = ((uint32_t(begin[0]) & 0x1F) << 6)
|
||||||
|
| ((uint32_t(begin[1]) & 0x3F));
|
||||||
|
} else if (first < 0xF0) {
|
||||||
|
ch = ((uint32_t(begin[0]) & 0x0F) << 12)
|
||||||
|
| ((uint32_t(begin[1]) & 0x3F) << 6)
|
||||||
|
| ((uint32_t(begin[2]) & 0x3F));
|
||||||
|
} else if (first < 0xF8) {
|
||||||
|
ch = ((uint32_t(begin[0]) & 0x07) << 18)
|
||||||
|
| ((uint32_t(begin[1]) & 0x3F) << 12)
|
||||||
|
| ((uint32_t(begin[2]) & 0x3F) << 6)
|
||||||
|
| ((uint32_t(begin[3]) & 0x3F));
|
||||||
|
} else {
|
||||||
|
// Invalid prefix
|
||||||
|
ch = uint32_t('?');
|
||||||
|
}
|
||||||
|
|
||||||
|
return begin + length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t* decodeTypedChar(
|
||||||
|
const uint16_t* begin,
|
||||||
|
const uint16_t* end,
|
||||||
|
uint32_t& ch) {
|
||||||
|
uint32_t first = begin[0];
|
||||||
|
|
||||||
|
if (likely(first < 0xD800)) {
|
||||||
|
ch = first;
|
||||||
|
return begin + 1;
|
||||||
|
} else if (first < 0xDC00) {
|
||||||
|
if (unlikely(begin + 2 > end)) {
|
||||||
|
ch = uint32_t('?');
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = 0x10000
|
||||||
|
+ ((uint32_t(begin[0]) & 0x3FF) << 10)
|
||||||
|
+ ((uint32_t(begin[1]) & 0x3FF));
|
||||||
|
return begin + 2;
|
||||||
|
} else if (unlikely(first < 0xE000)) {
|
||||||
|
// Stray low surrogate
|
||||||
|
ch = uint32_t('?');
|
||||||
|
return begin + 1;
|
||||||
|
} else {
|
||||||
|
ch = first;
|
||||||
|
return begin + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const uint32_t* decodeTypedChar(
|
||||||
|
const uint32_t* begin,
|
||||||
|
const uint32_t* end,
|
||||||
|
uint32_t& ch) {
|
||||||
|
ch = begin[0];
|
||||||
|
return begin + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint8_t* begin,
|
||||||
|
uint8_t* end,
|
||||||
|
uint32_t ch) {
|
||||||
|
if (likely(ch < 0x80)) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 1 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = uint8_t(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (ch < 0x800) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 2 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = uint8_t(0xC0 | (ch >> 6));
|
||||||
|
begin[1] = uint8_t(0x80 | (ch & 0x3F));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
} else if (ch < 0x10000) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 3 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = uint8_t(0xE0 | ((ch >> 12)));
|
||||||
|
begin[1] = uint8_t(0x80 | ((ch >> 6) & 0x3F));
|
||||||
|
begin[2] = uint8_t(0x80 | ((ch >> 0) & 0x3F));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
} else if (ch < 0x200000) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 4 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = uint8_t(0xF0 | ((ch >> 18)));
|
||||||
|
begin[1] = uint8_t(0x80 | ((ch >> 12) & 0x3F));
|
||||||
|
begin[2] = uint8_t(0x80 | ((ch >> 6) & 0x3F));
|
||||||
|
begin[3] = uint8_t(0x80 | ((ch >> 0) & 0x3F));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4;
|
||||||
|
} else {
|
||||||
|
// Invalid code point for UTF-8
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint16_t* begin,
|
||||||
|
uint16_t* end,
|
||||||
|
uint32_t ch) {
|
||||||
|
if (likely(ch < 0xD800)) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 1 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (ch < 0xE000) {
|
||||||
|
// Private use code points,
|
||||||
|
// we can't encode these
|
||||||
|
return 0;
|
||||||
|
} else if (ch < 0x10000) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 1 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (ch < 0x110000) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 2 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ch -= 0x10000;
|
||||||
|
begin[0] = uint16_t(0xD800 + (ch >> 10));
|
||||||
|
begin[1] = uint16_t(0xDC00 + (ch & 0x3FF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
// Invalid code point
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint32_t* begin,
|
||||||
|
uint32_t* end,
|
||||||
|
uint32_t ch) {
|
||||||
|
if (begin) {
|
||||||
|
if (unlikely(begin + 1 > end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
begin[0] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string fromws(const WCHAR* ws) {
|
||||||
|
size_t srcLen = length(ws);
|
||||||
|
size_t dstLen = transcodeString<char>(
|
||||||
|
nullptr, 0, ws, srcLen);
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
result.resize(dstLen);
|
||||||
|
|
||||||
|
transcodeString(result.data(),
|
||||||
|
dstLen, ws, srcLen);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::wstring tows(const char* mbs) {
|
||||||
|
size_t srcLen = length(mbs);
|
||||||
|
size_t dstLen = transcodeString<wchar_t>(
|
||||||
|
nullptr, 0, mbs, srcLen);
|
||||||
|
|
||||||
|
std::wstring result;
|
||||||
|
result.resize(dstLen);
|
||||||
|
|
||||||
|
transcodeString(result.data(),
|
||||||
|
dstLen, mbs, srcLen);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
242
src/util/util_string.h
Normal file
242
src/util/util_string.h
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "./com/com_include.h"
|
||||||
|
|
||||||
|
#include "util_bit.h"
|
||||||
|
#include "util_likely.h"
|
||||||
|
|
||||||
|
namespace dxvk::str {
|
||||||
|
|
||||||
|
template<size_t S> struct UnicodeChar { };
|
||||||
|
template<> struct UnicodeChar<1> { using type = uint8_t; };
|
||||||
|
template<> struct UnicodeChar<2> { using type = uint16_t; };
|
||||||
|
template<> struct UnicodeChar<4> { using type = uint32_t; };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using UnicodeCharType = typename UnicodeChar<sizeof(T)>::type;
|
||||||
|
|
||||||
|
const uint8_t* decodeTypedChar(
|
||||||
|
const uint8_t* begin,
|
||||||
|
const uint8_t* end,
|
||||||
|
uint32_t& ch);
|
||||||
|
|
||||||
|
const uint16_t* decodeTypedChar(
|
||||||
|
const uint16_t* begin,
|
||||||
|
const uint16_t* end,
|
||||||
|
uint32_t& ch);
|
||||||
|
|
||||||
|
const uint32_t* decodeTypedChar(
|
||||||
|
const uint32_t* begin,
|
||||||
|
const uint32_t* end,
|
||||||
|
uint32_t& ch);
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint8_t* begin,
|
||||||
|
uint8_t* end,
|
||||||
|
uint32_t ch);
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint16_t* begin,
|
||||||
|
uint16_t* end,
|
||||||
|
uint32_t ch);
|
||||||
|
|
||||||
|
size_t encodeTypedChar(
|
||||||
|
uint32_t* begin,
|
||||||
|
uint32_t* end,
|
||||||
|
uint32_t ch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decodes a single character
|
||||||
|
*
|
||||||
|
* Note that \c begin and \c end must not be equal.
|
||||||
|
* \param [in] begin Pointer to current position within the input string
|
||||||
|
* \param [in] end Pointer to the end of the input string
|
||||||
|
* \param [out] ch Pointer to the decoded character code
|
||||||
|
* \returns Pointer to next character in the input string
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
const T* decodeChar(
|
||||||
|
const T* begin,
|
||||||
|
const T* end,
|
||||||
|
uint32_t& ch) {
|
||||||
|
using CharType = UnicodeCharType<T>;
|
||||||
|
|
||||||
|
const CharType* result = decodeTypedChar(
|
||||||
|
reinterpret_cast<const CharType*>(begin),
|
||||||
|
reinterpret_cast<const CharType*>(end),
|
||||||
|
ch);
|
||||||
|
|
||||||
|
return reinterpret_cast<const T*>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Encodes a character
|
||||||
|
*
|
||||||
|
* Note that \c begin and \c end may be both be \c nullptr or equal, in
|
||||||
|
* which case only the length of the encoded character will be returned.
|
||||||
|
* \param [in] begin Pointer to current position within the output string
|
||||||
|
* \param [in] end Pointer to the end of the output string
|
||||||
|
* \param [in] ch Character to encode
|
||||||
|
* \returns If begin is \c nullptr , the number of units required to encode
|
||||||
|
* the character. Otherwise, the number of units written to the output.
|
||||||
|
* This may return \c 0 for characters that cannot be written or encoded.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
size_t encodeChar(
|
||||||
|
T* begin,
|
||||||
|
T* end,
|
||||||
|
uint32_t ch) {
|
||||||
|
using CharType = UnicodeCharType<T>;
|
||||||
|
|
||||||
|
return encodeTypedChar(
|
||||||
|
reinterpret_cast<CharType*>(begin),
|
||||||
|
reinterpret_cast<CharType*>(end),
|
||||||
|
ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Computes length of a null-terminated string
|
||||||
|
*
|
||||||
|
* \param [in] begin Start of input string
|
||||||
|
* \returns Number of characters in input string,
|
||||||
|
* excluding the terminating null character
|
||||||
|
*/
|
||||||
|
template<typename S>
|
||||||
|
size_t length(const S* string) {
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
while (string[result])
|
||||||
|
result += 1;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Converts string from one encoding to another
|
||||||
|
*
|
||||||
|
* The output string arguments may be \c nullptr. In that case, the
|
||||||
|
* total length of the transcoded string will be returned, in units
|
||||||
|
* of the output character type. The output string will only be
|
||||||
|
* null-terminated if the input string is also null-terminated.
|
||||||
|
* \tparam D Output character type
|
||||||
|
* \tparam S Input character type
|
||||||
|
* \param [in] dstBegin Start of output string
|
||||||
|
* \param [in] dstLength Length of output string
|
||||||
|
* \param [in] srcBegin Start of input string
|
||||||
|
* \param [in] srcLength Length of input string
|
||||||
|
* \returns If \c dstBegin is \c nullptr , the total number of output
|
||||||
|
* characters required to store the output string. Otherwise, the
|
||||||
|
* total number of characters written to the output string.
|
||||||
|
*/
|
||||||
|
template<typename D, typename S>
|
||||||
|
size_t transcodeString(
|
||||||
|
D* dstBegin,
|
||||||
|
size_t dstLength,
|
||||||
|
const S* srcBegin,
|
||||||
|
size_t srcLength) {
|
||||||
|
size_t totalLength = 0;
|
||||||
|
|
||||||
|
auto dstEnd = dstBegin + dstLength;
|
||||||
|
auto srcEnd = srcBegin + srcLength;
|
||||||
|
|
||||||
|
while (srcBegin < srcEnd) {
|
||||||
|
uint32_t ch;
|
||||||
|
|
||||||
|
srcBegin = decodeChar<S>(srcBegin, srcEnd, ch);
|
||||||
|
|
||||||
|
if (dstBegin)
|
||||||
|
totalLength += encodeChar<D>(dstBegin + totalLength, dstEnd, ch);
|
||||||
|
else
|
||||||
|
totalLength += encodeChar<D>(nullptr, nullptr, ch);
|
||||||
|
|
||||||
|
if (!ch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates string object from wide char array
|
||||||
|
*
|
||||||
|
* \param [in] ws Null-terminated wide string
|
||||||
|
* \returns Regular string object
|
||||||
|
*/
|
||||||
|
std::string fromws(const WCHAR* ws);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates wide string object from char array
|
||||||
|
*
|
||||||
|
* \param [in] mbs Null-terminated string
|
||||||
|
* \returns Wide string object
|
||||||
|
*/
|
||||||
|
std::wstring tows(const char* mbs);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
using path_string = std::wstring;
|
||||||
|
inline path_string topath(const char* mbs) { return tows(mbs); }
|
||||||
|
#else
|
||||||
|
using path_string = std::string;
|
||||||
|
inline path_string topath(const char* mbs) { return std::string(mbs); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline void format1(std::stringstream&) { }
|
||||||
|
|
||||||
|
template<typename... Tx>
|
||||||
|
void format1(std::stringstream& str, const WCHAR *arg, const Tx&... args) {
|
||||||
|
str << fromws(arg);
|
||||||
|
format1(str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void strlcpy(char* dst, const char* src, size_t count) {
|
||||||
|
if (count > 0) {
|
||||||
|
std::strncpy(dst, src, count - 1);
|
||||||
|
dst[count - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Split string at one or more delimiters characters
|
||||||
|
*
|
||||||
|
* \param [in] string String to split
|
||||||
|
* \param [in] delims Delimiter characters
|
||||||
|
* \returns Vector of substring views
|
||||||
|
*/
|
||||||
|
inline std::vector<std::string_view> split(std::string_view string, std::string_view delims = " ") {
|
||||||
|
std::vector<std::string_view> tokens;
|
||||||
|
|
||||||
|
for (size_t start = 0; start < string.size(); ) {
|
||||||
|
// Find first delimiter
|
||||||
|
const auto end = string.find_first_of(delims, start);
|
||||||
|
|
||||||
|
// Add non-empty tokens
|
||||||
|
if (start != end)
|
||||||
|
tokens.emplace_back(string.substr(start, end-start));
|
||||||
|
|
||||||
|
// Break at the end of string
|
||||||
|
if (end == std::string_view::npos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue