diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b54a270f8..2c870240e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,9 +2,9 @@ name: Build
on:
push:
- branches: [ main ]
+ branches: [ main, 'release/**' ]
pull_request:
- branches: [ main ]
+ branches: [ main, 'release/**' ]
jobs:
build-windows:
diff --git a/.github/workflows/test-build-windows.yml b/.github/workflows/test-build-windows.yml
new file mode 100644
index 000000000..67c3bfd6b
--- /dev/null
+++ b/.github/workflows/test-build-windows.yml
@@ -0,0 +1,52 @@
+name: Test Builds on Windows
+
+on: [push, pull_request, workflow_dispatch]
+
+jobs:
+ build-set-windows:
+ runs-on: windows-2022
+
+ steps:
+ - name: Checkout code
+ id: checkout-code
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+
+ - name: Setup glslangValidator
+ shell: pwsh
+ run: |
+ choco install vulkan-sdk -y
+ Write-Output "$([System.Environment]::GetEnvironmentVariable('VULKAN_SDK', 'Machine'))\Bin" `
+ | Out-File -FilePath "${Env:GITHUB_PATH}" -Append
+
+ - name: Setup Meson
+ shell: pwsh
+ run: pip install meson
+
+ - name: Find Visual Studio
+ shell: pwsh
+ run: |
+ $installationPath = Get-VSSetupInstance `
+ | Select-VSSetupInstance -Require Microsoft.VisualStudio.Workload.NativeDesktop -Latest `
+ | Select-Object -ExpandProperty InstallationPath
+ Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" `
+ | Out-File -FilePath "${Env:GITHUB_ENV}" -Append
+
+ - name: Build MSVC x86
+ shell: pwsh
+ run: |
+ & "${Env:COMSPEC}" /s /c "`"${Env:VSDEVCMD}`" -arch=x86 -host_arch=x64 -no_logo && set" `
+ | % { , ($_ -Split '=', 2) } `
+ | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) }
+ meson --buildtype release --backend vs2022 build-msvc-x86
+ msbuild -m build-msvc-x86/dxvk.sln
+
+ - name: Build MSVC x64
+ shell: pwsh
+ run: |
+ & "${Env:COMSPEC}" /s /c "`"${Env:VSDEVCMD}`" -arch=x64 -host_arch=x64 -no_logo && set" `
+ | % { , ($_ -Split '=', 2) } `
+ | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) }
+ meson --buildtype release --backend vs2022 build-msvc-x64
+ msbuild -m build-msvc-x64/dxvk.sln
diff --git a/.gitignore b/.gitignore
index 2b7c63153..003ac7fd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@
.vscode/
/external
**/_batch_output/**
+/public/bin/
diff --git a/README.md b/README.md
index 791711c39..d468bd246 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,8 @@ While dxvk-remix is a fork of DXVK, please report bugs encountered with dxvk-rem
## Project Documentation
- [Rtx Options](/RtxOptions.md)
-- [Terrain System](/documentation/TerrainSystem.md)
- [Anti-Culling System](/documentation/AntiCullingSystem.md)
- [Foliage System](/documentation/FoliageSystem.md)
+- [Opacity Micromap](/documentation/OpacityMicromap.md)
+- [Terrain System](/documentation/TerrainSystem.md)
- [Unit Test](/documentation/UnitTest.md)
diff --git a/RELEASE b/RELEASE
index 9442d91d0..45fb4f951 100644
--- a/RELEASE
+++ b/RELEASE
@@ -20,4 +20,4 @@
# DEALINGS IN THE SOFTWARE.
#############################################################################
-1.9.3
\ No newline at end of file
+1.9.4
diff --git a/RtxOptions.md b/RtxOptions.md
index be2ac49f9..a63f24fa4 100644
--- a/RtxOptions.md
+++ b/RtxOptions.md
@@ -117,6 +117,7 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.debugView.composite.compositeViewIdx|int|0|Index of a composite view to show when Composite Debug View is enabled\. The index must be a a valid value from CompositeDebugView enumeration\. Value of 0 disables Composite Debug View\.|
|rtx.debugView.debugViewIdx|int|0|Index of a debug view to show when Debug View is enabled\. The index must be a valid value from DEBUG\_VIEW\_\* macro defined indices\. Value of 0 disables Debug View\.|
|rtx.debugView.displayType|int|0||
+|rtx.debugView.enableGammaCorrection|bool|False|Enables gamma correction of a debug view value\.|
|rtx.debugView.enablePseudoColor|bool|False|Enables RGB color coding of a scalar debug view value\.|
|rtx.debugView.evMaxValue|int|4||
|rtx.debugView.evMinValue|int|-4||
@@ -167,7 +168,11 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.di.stealBoundaryPixelSamplesWhenOutsideOfScreen|bool|True|Steal screen boundary samples when a hit point is outside the screen\.|
|rtx.displacement.displacementFactor|float|1|Scaling factor for all displacement maps|
|rtx.displacement.enableDirectLighting|bool|True|Whether direct lighting accounts for displacement mapping|
+|rtx.displacement.enableIndirectHit|bool|False|Whether indirect ray hits account for displacement mapping \(Enabling this is expensive\. Without it, non\-perfect reflections of displaced objects will not show displacement\.\)|
|rtx.displacement.enableIndirectLighting|bool|True|Whether indirect lighting accounts for displacement mapping|
+|rtx.displacement.enableNEECache|bool|True|Whether the NEE cache accounts for displacement mapping|
+|rtx.displacement.enablePSR|bool|False|Enable PSR \(perfect reflections\) for materials with displacement\. Rays that have been perfectly reflected off a POM surface will not collide correctly with other parts of that same surface\.|
+|rtx.displacement.enableReSTIRGI|bool|True|Whether ReSTIR GI accounts for displacement mapping|
|rtx.dlfg.enable|bool|True|Enables DLSS 3\.0 frame generation which generates interpolated frames to increase framerate at the cost of slightly more latency\.|
|rtx.dlssEnhancementDirectLightMaxValue|float|10|The maximum strength of direct lighting enhancement\.|
|rtx.dlssEnhancementDirectLightPower|float|0.7|The overall strength of direct lighting enhancement\.|
@@ -312,6 +317,7 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.lightConversionEqualityDirectionThreshold|float|0.99|The lower cosine angle threshold between two directions used to determine if two directional lights as the same light when uniquely identifying legacy lights for conversion\.|
|rtx.lightConversionEqualityDistanceThreshold|float|0.05|The upper distance threshold between two positions used to determine if two positional lights as the same light when uniquely identifying legacy lights for conversion\.|
|rtx.lightConversionSphereLightFixedRadius|float|4|The fixed radius in world units to use for legacy lights converted to sphere lights \(currently point and spot lights will convert to sphere lights\)\. Use caution with large light radii as many legacy lights will be placed close to geometry and intersect it, causing suboptimal light sampling performance or other visual artifacts \(lights clipping through walls, etc\)\.|
+|rtx.lights.enableDebugMode|bool|False|Enables light debug visualization\.|
|rtx.localtonemap.boostLocalContrast|bool|False|Boosts contrast on local features\.|
|rtx.localtonemap.displayMip|int|0|Bottom mip level of tone map pyramid\.|
|rtx.localtonemap.exposure|float|0.75|Exposure factor applied on average exposure\.|
@@ -535,7 +541,8 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.stochasticAlphaBlendShareNeighbors|bool|True|Share result with other pixels to accelerate search\.|
|rtx.stochasticAlphaBlendUseNeighborSearch|bool|True|Get radiance from neighbor opaque pixels\.|
|rtx.stochasticAlphaBlendUseRadianceVolume|bool|True|Get radiance from radiance volume\.|
-|rtx.subsurface.enableThinOpaque|bool|True|Enable thin opaque material\. The materials with th in opaque properties will fallback to normal opaque material\.|
+|rtx.subsurface.enableThinOpaque|bool|True|Enable thin opaque material\. The materials withthin opaque properties will fallback to normal opaque material\.|
+|rtx.subsurface.enableTextureMaps|bool|True|Enable texture maps such as thickness map or scattering albedo map\. The corresponding subsurface properties will fallback to per\-material constants if this is disabled\.|
|rtx.subsurface.surfaceThicknessScale|float|1|Scalar of the subsurface thickness\.|
|rtx.taauPreset|int|1|Adjusts TAA\-U scaling factor, trades quality for performance\.|
|rtx.temporalAA.colorClampingFactor|float|1|A scalar factor to apply to the standard deviation of the neighborhood of pixels in the color signal used for clamping\. Should be in the range 0\-infinity\.
This value essentially represents how many standard deviations of tolerance from the current frame's colors around each pixel pixel the temporally accumulated color signal may have\.
Higher values will cause more ghosting whereas lower values may reduce ghosting but will impact image quality \(less ability to upscale effectively\) and reduce stability \(more jittering\)\.|
diff --git a/documentation/FoliageSystem.md b/documentation/FoliageSystem.md
index 3a67c7a0f..223c41504 100644
--- a/documentation/FoliageSystem.md
+++ b/documentation/FoliageSystem.md
@@ -10,6 +10,10 @@ User Instructions:
b. [Subsurface Measurement Distance]: The thickness of the foliage surface in the range [0, 16]. Incidence radiance is attenuated proportionally to the thickness. The unit is [mm].
c. [Subsurface Single Scattering Albedo]: The coefficient determines how much energy is scattered when trace through subsurface materials.
d. [Subsurface Volumetric Anisotropy]: The anisotropy of the scattering phase function (-1 being backscattering, 0 being isotropic, 1 being forward scattering).
+4. Optional: Setup Foliage Texture Maps:
+ a. [Subsurface Transmittance Color Map]
+ b. [Subsurface Measurement Distance Map]
+ c. [Subsurface Single Scattering Albedo Map]
Real-time debug interface:
1. Debugging View: Enable [Is Thin Opaque](../RtxOptions.md) or [rtx.debugView.debugViewIdx = 800] to verify if thin opaque materials are correctly set up.
diff --git a/documentation/OpacityMicromap.md b/documentation/OpacityMicromap.md
new file mode 100644
index 000000000..e64534aa1
--- /dev/null
+++ b/documentation/OpacityMicromap.md
@@ -0,0 +1,14 @@
+# Opacity Micromap
+
+Opacity Micromap (OMM) optimizes ray tracing by skipping transparent surface ray interactions due to opacity texture cutouts. OMMs encode opacity at a microtriangle level in a triangle and there can be thousands of microtriangles generated per a triangle. This is similar to many texels from a texture being applied to a triangle. OMMs need to be pre-built for a triangle and are encoded during a bottom level acceleration structure (BLAS) construction for a GPU driver to use during ray tracing. Any transparent parts of the surface due to opacity texture and/or texture and vertex blending will be skipped by the driver. Without OMMs such hits would be returned as opaque hits to the ray tracing shader code and depend on the ray tracer to resolve the opacity and skip the hit, returning the execution to continue ray tracing to the driver. OMMs avoid this unnecessary roundtrip altogether and, thus, provide a performance speed up. In cases, where there is a lot of triangles with opacity cutouts and, especially, with multiple layers of such geometry (i.e. for fences, particles, foliage, etc.) the performance uplift in Remix can be in the order of 10% or higher. While older NVIDIA architectures benefit from this feature as well, the speed ups due to OMMs is higher on Ada (40**) GPUs.
+
+OMMs are enabled by default in Remix and generated at runtime. This can be controlled via GUI or rtx.conf parameter [rtx.opacityMicromap.enable](../RtxOptions.md). OMMs are generated at runtime at a throttled pace so as to limit amount of GPU resources being used for them. It can take several seconds to generate OMMs for a scene. OMMs require enough free VRAM to get generated and, thus, their generation is dependant on enough VRAM being available. Disabling OMMs also releases the generated OMMs. Therefore, if you want to double check performance impact ON/OFF, it is advised to simply disable OMMs being bound via [rtx.opacityMicromap.enableBinding](../RtxOptions.md) so that they are not release in the process.
+
+To take the best advantage of OMMs, author your assets considering following:
+- Make transparent regions of assets resolve to opacity of 0 (i.e. fully transparent).
+- Reuse UVs across triangles so that a same OMM can apply to multiple triangles in a geometry. Repeating textures are your friend. This reduces OMM build times and runtime memory cost.
+- Avoid thin triangles.
+- Avoid generating triangles with very high frequency of opaque and transparent regions changing back and forth across a triangle (i.e. a mesh in a high density fence, but represented with only few triangles). In such cases, tesselate your geometry further to lower the opacity frequency within a triangle. OMMs in Remix are generated roughly at up to 256x256 microtriangles per triangle. It is important that a good portion of such microtriangles correspond to fully transparent regions. If the underlying opacity for a triangle has a higher frequency of opaque and transparent features than the 256x256 and microtriangles cover areas that are both opaque and transparent, such microtriangles will be marked as non-transparent. In that case, there will be no speed up since the geometry is essentially treated as opaque by the driver. Therefore, make sure your geometry is tessellated enough such that opaque/transparent regions appear at a frequency lower than that and, preferably, end up with multiple neighboring microtriangles falling into the same opaque/transparent category.
+
+Caveats:
+- Similar to other instance tracking cases in Remix, the runtime hashes OMM signatures and uses the hashes to differentiate among the OMMs. However, although it's rarely the case, but should two OMMs hash collide they will be treated as equal. This will result in an incorrect OMM being bound for a triangle and, thus, having wrong opacity cutouts being applied. In that case, a triangle can appear transparent in areas where it should be opaque. If that happens, tag such texture as [rtx.opacityMicromapIgnoreTextures](../RtxOptions.md) to avoid generating OMMs for them and report this as an issue on github.
\ No newline at end of file
diff --git a/dxvk.conf b/dxvk.conf
index 3fcdd67e7..f50dfd991 100644
--- a/dxvk.conf
+++ b/dxvk.conf
@@ -365,7 +365,7 @@ d3d9.adapterOverride = 0
# Supported values:
# - True/False
-# d3d9.floatEmulation =
+# d3d9.floatEmulation = Auto
# Enable dialog box mode
diff --git a/meson.build b/meson.build
index 42c0efd28..ef4ec6f6c 100644
--- a/meson.build
+++ b/meson.build
@@ -155,6 +155,9 @@ if not dxvk_is_msvc
endif
endif
+remix_api_include_path = include_directories('./public/include')
+add_global_arguments('/DREMIX_LIBRARY_EXPORTS=1', language : 'cpp')
+
dxvk_include_dirs = [
'./include',
'./include/vulkan/include'
@@ -391,6 +394,10 @@ boost_include_path = include_directories('external/nv_usd/include/boost-1_78/')
usd_include_paths = [nv_usd_include_path, boost_include_path]
+vk_include_path = include_directories('./include/vulkan/include')
+
+lssusd_include_paths = [nv_usd_include_path, boost_include_path, vk_include_path]
+
nvapi_include_paths = include_directories(join_paths(meson.global_source_root(), 'external/nvapi'))
nvapi_lib_path = join_paths(meson.global_source_root(), 'external/nvapi/amd64')
nvapi_lib = dxvk_compiler.find_library('nvapi64', dirs : nvapi_lib_path)
diff --git a/packman-external.xml b/packman-external.xml
index 197cac5c6..4e5106a12 100644
--- a/packman-external.xml
+++ b/packman-external.xml
@@ -20,7 +20,7 @@
-
+
@@ -41,6 +41,6 @@
-
+
diff --git a/public/include/remix/remix.h b/public/include/remix/remix.h
new file mode 100644
index 000000000..0c0458f05
--- /dev/null
+++ b/public/include/remix/remix.h
@@ -0,0 +1,734 @@
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "remix_c.h"
+
+#include
+#include
+#include
+#include
+
+#ifndef REMIXAPI_ASSERT
+#include
+#define REMIXAPI_ASSERT(mustBeTrue) assert(mustBeTrue)
+#endif
+
+namespace remix {
+ namespace detail {
+ template< typename T >
+ struct Result {
+ Result(T&& value)
+ : m_value { std::forward< T >(value) }
+ , m_status { REMIXAPI_ERROR_CODE_SUCCESS } {
+ }
+
+ Result(remixapi_ErrorCode error)
+ : m_value {}
+ , m_status { error } {
+ REMIXAPI_ASSERT(error != REMIXAPI_ERROR_CODE_SUCCESS);
+ }
+
+ Result(const Result&) = delete;
+ Result(Result&&) = delete;
+ Result& operator=(const Result&) = delete;
+ Result& operator=(Result&&) = delete;
+
+ operator bool() const {
+ return m_status == REMIXAPI_ERROR_CODE_SUCCESS;
+ }
+
+ T& value() {
+ REMIXAPI_ASSERT(bool { *this });
+ return m_value;
+ }
+
+ const T& value() const {
+ REMIXAPI_ASSERT(bool { *this });
+ return m_value;
+ }
+
+ remixapi_ErrorCode status() const {
+ return m_status;
+ }
+
+ const T& operator*() const {
+ return value();
+ }
+ T& operator*() {
+ return value();
+ }
+ const T* operator->() const {
+ return &value();
+ }
+ T* operator->() {
+ return &value();
+ }
+
+ private:
+ const remixapi_ErrorCode m_status;
+ T m_value;
+ };
+
+ template<>
+ struct Result< void > {
+ Result(remixapi_ErrorCode error) : m_status { error } { }
+
+ Result(const Result&) = delete;
+ Result(Result&&) = delete;
+ Result& operator=(const Result&) = delete;
+ Result& operator=(Result&&) = delete;
+
+ operator bool() const {
+ return m_status == REMIXAPI_ERROR_CODE_SUCCESS;
+ }
+
+ remixapi_ErrorCode status() const {
+ return m_status;
+ }
+
+ private:
+ const remixapi_ErrorCode m_status;
+ };
+
+ template< typename T >
+ void assign_if(remixapi_Bool& hasvalue, T& value, const std::optional< T >& src) {
+ if (src) {
+ hasvalue = true;
+ value = src.value();
+ } else {
+ hasvalue = false;
+ }
+ }
+ }
+
+ template< typename T >
+ using Result = detail::Result< T >;
+
+ using StructType = remixapi_StructType;
+ using Float2D = remixapi_Float2D;
+ using Float3D = remixapi_Float3D;
+ using Float4D = remixapi_Float4D;
+ using Transform = remixapi_Transform;
+
+
+
+ struct MaterialInfo;
+ struct MeshInfo;
+ struct CameraInfo;
+ struct InstanceInfo;
+ struct LightInfo;
+ namespace detail {
+ struct dxvk_ExternalSwapchain;
+ struct dxvk_VkImage;
+ }
+
+ struct Interface {
+ HMODULE m_RemixDLL { nullptr };
+ remixapi_Interface m_CInterface {};
+
+ // Functions
+ Result< void > Shutdown();
+ Result< remixapi_MaterialHandle > CreateMaterial(const MaterialInfo& info);
+ Result< void > DestroyMaterial(remixapi_MaterialHandle handle);
+ Result< remixapi_MeshHandle > CreateMesh(const MeshInfo& info);
+ Result< void > DestroyMesh(remixapi_MeshHandle handle);
+ Result< void > SetupCamera(const CameraInfo& info);
+ Result< void > DrawInstance(const InstanceInfo& info);
+ Result< remixapi_LightHandle > CreateLight(const LightInfo& info);
+ Result< void > DestroyLight(remixapi_LightHandle handle);
+ Result< void > DrawLightInstance(remixapi_LightHandle handle);
+ Result< void > SetConfigVariable(const char* key, const char* value);
+
+ // DXVK interoperability
+ Result< IDirect3D9Ex* > dxvk_CreateD3D9(bool disableSrgbConversionForOutput);
+ Result< void > dxvk_RegisterD3D9Device(IDirect3DDevice9Ex* d3d9Device);
+ Result< detail::dxvk_ExternalSwapchain > dxvk_GetExternalSwapchain();
+ Result< detail::dxvk_VkImage > dxvk_GetVkImage(IDirect3DSurface9* source);
+ Result< void > dxvk_CopyRenderingOutput(IDirect3DSurface9* destination,
+ remixapi_dxvk_CopyRenderingOutputType type);
+ };
+
+ namespace lib {
+ // Helper function to load a .dll of Remix, and initialize it.
+ // pRemixD3D9DllPath is a path to .dll file, e.g. "C:\dxvk-remix-nv\public\bin\d3d9.dll"
+ // TODO: wchar_t / char
+ [[nodiscard]] inline Result< Interface > loadRemixDllAndInitialize(const char* pRemixD3D9DllPath) {
+ {
+ auto lastSlash = std::string_view { pRemixD3D9DllPath }.find_last_of("/\\");
+ if (lastSlash != std::string::npos) {
+ SetDllDirectoryA(
+ std::string { std::string_view{ pRemixD3D9DllPath }.substr(0, lastSlash) }
+ .c_str());
+ }
+ }
+
+ if (HMODULE remixDll = LoadLibraryA(pRemixD3D9DllPath)) {
+ auto pfn_InitializeLibrary = reinterpret_cast(
+ GetProcAddress(remixDll, "remixapi_InitializeLibrary"));
+
+ if (pfn_InitializeLibrary) {
+ remixapi_InitializeLibraryInfo info = {};
+ {
+ info.sType = REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO;
+ info.version = REMIXAPI_VERSION_MAKE(REMIXAPI_VERSION_MAJOR,
+ REMIXAPI_VERSION_MINOR,
+ REMIXAPI_VERSION_PATCH);
+ }
+
+ remixapi_Interface interfaceInC = {};
+ remixapi_ErrorCode status = pfn_InitializeLibrary(&info, &interfaceInC);
+
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+
+ remix::Interface interfaceInCpp = {};
+ {
+ interfaceInCpp.m_RemixDLL = remixDll;
+ interfaceInCpp.m_CInterface = interfaceInC;
+ }
+ return interfaceInCpp;
+ }
+
+ return REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE;
+ }
+
+ return REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE;
+ }
+
+ inline void shutdownAndUnloadRemixDll(Interface& interfaceInCpp) {
+ interfaceInCpp.Shutdown();
+ if (interfaceInCpp.m_RemixDLL) {
+ FreeLibrary(interfaceInCpp.m_RemixDLL);
+ }
+ interfaceInCpp = {};
+ }
+ }
+
+
+
+ inline Result< void > Interface::Shutdown() {
+ if (m_CInterface.Shutdown) {
+ return m_CInterface.Shutdown();
+ }
+ return REMIXAPI_ERROR_CODE_SUCCESS;
+ }
+
+ inline Result< void > Interface::SetConfigVariable(const char* key, const char* value) {
+ return m_CInterface.SetConfigVariable(key, value);
+ }
+
+
+
+ struct MaterialInfoOpaqueEXT : remixapi_MaterialInfoOpaqueEXT {
+ MaterialInfoOpaqueEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT;
+ pNext = nullptr;
+ roughnessTexture = {};
+ metallicTexture = {};
+ anisotropy = 0.0f;
+ albedoConstant = { 1.0f, 1.0f, 1.0f };
+ opacityConstant = 1.0f;
+ roughnessConstant = 1.0f;
+ metallicConstant = 0.0f;
+ thinFilmThickness_hasvalue = false;
+ thinFilmThickness_value = 200.f;
+ alphaIsThinFilmThickness = false;
+ heightTexture = {};
+ heightTextureStrength = 0.0f;
+ useDrawCallAlphaState = true;
+ blendType_hasvalue = false;
+ blendType_value = 0;
+ invertedBlend = false;
+ alphaTestType = 7;
+ alphaReferenceValue = 0;
+ static_assert(sizeof remixapi_MaterialInfoOpaqueEXT == 112);
+ }
+
+ void set_roughnessTexture(std::filesystem::path v) {
+ cpp_roughnessTexture = std::move(v);
+ roughnessTexture = cpp_roughnessTexture.c_str();
+ }
+ void set_metallicTexture(std::filesystem::path v) {
+ cpp_metallicTexture = std::move(v);
+ metallicTexture = cpp_metallicTexture.c_str();
+ }
+ void set_heightTexture(std::filesystem::path v) {
+ cpp_heightTexture = std::move(v);
+ heightTexture = cpp_heightTexture.c_str();
+ }
+ void set_thinFilmThickness(const std::optional< float >& v) {
+ detail::assign_if(thinFilmThickness_hasvalue, thinFilmThickness_value, v);
+ }
+ void set_blendType(const std::optional< int >& v) {
+ detail::assign_if(blendType_hasvalue, blendType_value, v);
+ }
+
+ private:
+ std::filesystem::path cpp_roughnessTexture {};
+ std::filesystem::path cpp_metallicTexture {};
+ std::filesystem::path cpp_heightTexture {};
+ };
+
+ // Can be linked to MaterialInfoOpaqueEXT
+ struct MaterialInfoOpaqueSubsurfaceEXT : remixapi_MaterialInfoOpaqueSubsurfaceEXT {
+ MaterialInfoOpaqueSubsurfaceEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT;
+ pNext = nullptr;
+ subsurfaceTransmittanceTexture = {};
+ subsurfaceThicknessTexture = {};
+ subsurfaceSingleScatteringAlbedoTexture = {};
+ subsurfaceTransmittanceColor = { 0.5f, 0.5f, 0.5f };
+ subsurfaceMeasurementDistance = 0.0f;
+ subsurfaceSingleScatteringAlbedo = { 0.5f, 0.5f, 0.5f };;
+ subsurfaceVolumetricAnisotropy = 0.0f;
+ static_assert(sizeof remixapi_MaterialInfoOpaqueSubsurfaceEXT == 72);
+ }
+
+ void set_subsurfaceTransmittanceTexture(std::filesystem::path v) {
+ cpp_subsurfaceTransmittanceTexture = std::move(v);
+ subsurfaceTransmittanceTexture = cpp_subsurfaceTransmittanceTexture.c_str();
+ }
+ void set_subsurfaceThicknessTexture(std::filesystem::path v) {
+ cpp_subsurfaceThicknessTexture = std::move(v);
+ subsurfaceThicknessTexture = cpp_subsurfaceThicknessTexture.c_str();
+ }
+ void set_subsurfaceSingleScatteringAlbedoTexture(std::filesystem::path v) {
+ cpp_subsurfaceSingleScatteringAlbedoTexture = std::move(v);
+ subsurfaceSingleScatteringAlbedoTexture = cpp_subsurfaceSingleScatteringAlbedoTexture.c_str();
+ }
+
+ private:
+ std::filesystem::path cpp_subsurfaceTransmittanceTexture {};
+ std::filesystem::path cpp_subsurfaceThicknessTexture {};
+ std::filesystem::path cpp_subsurfaceSingleScatteringAlbedoTexture {};
+ };
+
+ struct MaterialInfoTranslucentEXT : remixapi_MaterialInfoTranslucentEXT {
+ MaterialInfoTranslucentEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT;
+ pNext = nullptr;
+ transmittanceTexture = {};
+ refractiveIndex = 1.3f;
+ transmittanceColor = { 0.97f, 0.97f, 0.97f };
+ transmittanceMeasurementDistance = 1.0f;
+ thinWallThickness_hasvalue = false;
+ thinWallThickness_value = 0.001f;
+ useDiffuseLayer = false;
+ static_assert(sizeof remixapi_MaterialInfoTranslucentEXT == 56);
+ }
+
+ void set_transmittanceTexture(std::filesystem::path v) {
+ cpp_transmittanceTexture = std::move(v);
+ transmittanceTexture = cpp_transmittanceTexture.c_str();
+ }
+ void set_thinWallThickness(const std::optional< float >& v) {
+ detail::assign_if(thinWallThickness_hasvalue, thinWallThickness_value, v);
+ }
+
+ private:
+ std::filesystem::path cpp_transmittanceTexture {};
+ };
+
+ struct MaterialInfoPortalEXT : remixapi_MaterialInfoPortalEXT {
+ MaterialInfoPortalEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT;
+ pNext = nullptr;
+ rayPortalIndex = 0;
+ rotationSpeed = 0.0f;
+ static_assert(sizeof remixapi_MaterialInfoPortalEXT == 24);
+ }
+ };
+
+ struct MaterialInfo : remixapi_MaterialInfo {
+ MaterialInfo() {
+ sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO;
+ pNext = nullptr;
+ hash = 0;
+ albedoTexture = {};
+ normalTexture = {};
+ tangentTexture = {};
+ emissiveTexture = {};
+ emissiveIntensity = 0.0f;
+ emissiveColorConstant = { 0.0f, 0.0f, 0.0f };
+ spriteSheetRow = 1;
+ spriteSheetCol = 1;
+ spriteSheetFps = 0;
+ filterMode = 1; // Linear
+ wrapModeU = 0; // Repeat
+ wrapModeV = 0; // Repeat
+ static_assert(sizeof remixapi_MaterialInfo == 80);
+ }
+
+ void set_albedoTexture(std::filesystem::path v) {
+ cpp_albedoTexture = std::move(v);
+ albedoTexture = cpp_albedoTexture.c_str();
+ }
+ void set_normalTexture(std::filesystem::path v) {
+ cpp_normalTexture = std::move(v);
+ normalTexture = cpp_normalTexture.c_str();
+ }
+ void set_tangentTexture(std::filesystem::path v) {
+ cpp_tangentTexture = std::move(v);
+ tangentTexture = cpp_tangentTexture.c_str();
+ }
+ void set_emissiveTexture(std::filesystem::path v) {
+ cpp_emissiveTexture = std::move(v);
+ emissiveTexture = cpp_emissiveTexture.c_str();
+ }
+
+ private:
+ std::filesystem::path cpp_albedoTexture {};
+ std::filesystem::path cpp_normalTexture {};
+ std::filesystem::path cpp_tangentTexture {};
+ std::filesystem::path cpp_emissiveTexture {};
+ };
+
+ inline Result< remixapi_MaterialHandle > Interface::CreateMaterial(const MaterialInfo& info) {
+ remixapi_MaterialHandle handle = nullptr;
+ remixapi_ErrorCode status = m_CInterface.CreateMaterial(&info, &handle);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return handle;
+ }
+
+ inline Result< void > Interface::DestroyMaterial(remixapi_MaterialHandle handle) {
+ return m_CInterface.DestroyMaterial(handle);
+ }
+
+
+
+ struct MeshInfo : remixapi_MeshInfo {
+ MeshInfo() {
+ sType = REMIXAPI_STRUCT_TYPE_MESH_INFO;
+ pNext = nullptr;
+ hash = 0;
+ surfaces_values = {};
+ surfaces_count = 0;
+ static_assert(sizeof remixapi_MeshInfo == 40);
+ }
+ };
+
+ inline Result< remixapi_MeshHandle > Interface::CreateMesh(const MeshInfo& info) {
+ remixapi_MeshHandle handle = nullptr;
+ remixapi_ErrorCode status = m_CInterface.CreateMesh(&info, &handle);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return handle;
+ }
+
+ inline Result< void > Interface::DestroyMesh(remixapi_MeshHandle handle) {
+ return m_CInterface.DestroyMesh(handle);
+ }
+
+
+
+ using CameraType = remixapi_CameraType;
+
+ // Ignores view / projection matrices from CameraInfo
+ // by recalculating them from the given arguments in this struct.
+ struct CameraInfoParameterizedEXT : remixapi_CameraInfoParameterizedEXT {
+ CameraInfoParameterizedEXT() {
+ sType = { REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT };
+ pNext = { nullptr };
+ position = { 0, 0, 0 };
+ forward = { 0, 0, 1 };
+ up = { 0, 1, 0 };
+ right = { 1, 0, 0 };
+ fovYInDegrees = 75.f;
+ aspect = 16.f / 9.f;
+ nearPlane = 0.1f;
+ farPlane = 1000.f;
+ static_assert(sizeof remixapi_CameraInfoParameterizedEXT == 80);
+ }
+ };
+
+ struct CameraInfo : remixapi_CameraInfo {
+ CameraInfo() {
+ sType = { REMIXAPI_STRUCT_TYPE_CAMERA_INFO };
+ pNext = { nullptr };
+ type = { REMIXAPI_CAMERA_TYPE_WORLD };
+ view[0][0] = view[1][1] = view[2][2] = view[3][3] = 1.f;
+ projection[0][0] = projection[1][1] = projection[2][2] = projection[3][3] = 1.f;
+ static_assert(sizeof remixapi_CameraInfo == 152);
+ }
+ };
+
+ inline Result< void > Interface::SetupCamera(const CameraInfo& info) {
+ return m_CInterface.SetupCamera(&info);
+ }
+
+
+
+ struct InstanceInfoBoneTransformsEXT : remixapi_InstanceInfoBoneTransformsEXT {
+ InstanceInfoBoneTransformsEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT;
+ pNext = nullptr;
+ boneTransforms_count = 0;
+ boneTransforms_values = {};
+ static_assert(sizeof remixapi_InstanceInfoBoneTransformsEXT == 32);
+ }
+ };
+
+ struct InstanceInfoBlendEXT : remixapi_InstanceInfoBlendEXT {
+ InstanceInfoBlendEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT;
+ pNext = nullptr;
+ alphaTestEnabled = false;
+ alphaTestReferenceValue = 0;
+ alphaTestCompareOp = 7 /* VK_COMPARE_OP_ALWAYS */;
+ alphaBlendEnabled = false;
+ srcColorBlendFactor = 1 /* VK_BLEND_FACTOR_ONE */;
+ dstColorBlendFactor = 0 /* VK_BLEND_FACTOR_ZERO */;
+ colorBlendOp = 0 /* VK_BLEND_OP_ADD */;
+ textureColorArg1Source = 1 /* RtTextureArgSource::Texture */;
+ textureColorArg2Source = 0 /* RtTextureArgSource::None */;
+ textureColorOperation = 3 /* DxvkRtTextureOperation::Modulate */;
+ textureAlphaArg1Source = 1 /* RtTextureArgSource::Texture */;
+ textureAlphaArg2Source = 0 /* RtTextureArgSource::None */;
+ textureAlphaOperation = 1 /* DxvkRtTextureOperation::SelectArg1 */;
+ tFactor = 0XFFFFFFFF;
+ isTextureFactorBlend = false;
+ static_assert(sizeof remixapi_InstanceInfoBlendEXT == 80);
+ }
+ };
+
+ struct InstanceInfoObjectPickingEXT : remixapi_InstanceInfoObjectPickingEXT {
+ InstanceInfoObjectPickingEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT;
+ pNext = nullptr;
+ objectPickingValue = 0;
+ }
+ };
+
+ using InstanceCategoryBit = remixapi_InstanceCategoryBit;
+ using InstanceCategoryFlags = remixapi_InstanceCategoryFlags;
+
+ struct InstanceInfo : remixapi_InstanceInfo {
+ InstanceInfo() {
+ sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO;
+ pNext = nullptr;
+ categoryFlags = 0;
+ mesh = 0;
+ transform = {};
+ doubleSided = false;
+ static_assert(sizeof remixapi_InstanceInfo == 88);
+ }
+ };
+
+ inline Result< void > Interface::DrawInstance(const InstanceInfo& info) {
+ return m_CInterface.DrawInstance(&info);
+ }
+
+
+
+ namespace detail {
+ inline remixapi_LightInfoLightShaping defaultLightShaping() {
+ remixapi_LightInfoLightShaping shaping {};
+ {
+ shaping.primaryAxis = { 0.0f, 0.0f, 1.0f };
+ shaping.coneAngleDegrees = 180.0f;
+ shaping.coneSoftness = 0.0f;
+ shaping.focusExponent = 0.0f;
+ }
+ return shaping;
+ };
+ }
+
+ using LightInfoLightShaping = remixapi_LightInfoLightShaping;
+
+ struct LightInfoSphereEXT : remixapi_LightInfoSphereEXT {
+ LightInfoSphereEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT;
+ pNext = nullptr;
+ position = { 0.0f, 0.0f, 0.0f };
+ radius = 0.05f;
+ shaping_hasvalue = false;
+ shaping_value = detail::defaultLightShaping();
+ static_assert(sizeof remixapi_LightInfoSphereEXT == 64);
+ }
+
+ void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) {
+ detail::assign_if(shaping_hasvalue, shaping_value, v);
+ }
+ };
+
+ struct LightInfoRectEXT : remixapi_LightInfoRectEXT {
+ LightInfoRectEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT;
+ pNext = nullptr;
+ position = { 0.0f, 0.0f, 0.0f };
+ xAxis = { 1.0f, 0.0f, 0.0f };
+ xSize = 1.0f;
+ yAxis = { 0.0f, 1.0f, 0.0f };
+ ySize = 1.0f;
+ shaping_hasvalue = false;
+ shaping_value = detail::defaultLightShaping();
+ static_assert(sizeof remixapi_LightInfoRectEXT == 88);
+ }
+
+ void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) {
+ detail::assign_if(shaping_hasvalue, shaping_value, v);
+ }
+ };
+
+ struct LightInfoDiskEXT : remixapi_LightInfoDiskEXT {
+ LightInfoDiskEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT;
+ pNext = nullptr;
+ position = { 0.0f, 0.0f, 0.0f };
+ xAxis = { 1.0f, 0.0f, 0.0f };
+ xRadius = 1.0f;
+ yAxis = { 0.0f, 1.0f, 0.0f };
+ yRadius = 1.0f;
+ shaping_hasvalue = false;
+ shaping_value = detail::defaultLightShaping();
+ static_assert(sizeof remixapi_LightInfoDiskEXT == 88);
+ }
+
+ void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) {
+ detail::assign_if(shaping_hasvalue, shaping_value, v);
+ }
+ };
+
+ struct LightInfoCylinderEXT : remixapi_LightInfoCylinderEXT {
+ LightInfoCylinderEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT;
+ pNext = nullptr;
+ position = { 0.0f, 0.0f, 0.0f };
+ radius = 1.0f;
+ axis = { 1.0f, 0.0f, 0.0f };
+ axisLength = 1.0f;
+ static_assert(sizeof remixapi_LightInfoCylinderEXT == 48);
+ }
+ };
+
+ struct LightInfoDistantEXT : remixapi_LightInfoDistantEXT {
+ LightInfoDistantEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT;
+ pNext = nullptr;
+ direction = { 0.0f, -1.0f, 0.0f };
+ angularDiameterDegrees = 0.5f;
+ static_assert(sizeof remixapi_LightInfoDistantEXT == 32);
+ }
+ };
+
+ struct LightInfoDomeEXT : remixapi_LightInfoDomeEXT {
+ LightInfoDomeEXT() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT;
+ pNext = nullptr;
+ transform = {};
+ colorTexture = {};
+ static_assert(sizeof remixapi_LightInfoDomeEXT == 72);
+ }
+
+ void set_colorTexture(std::filesystem::path v) {
+ cpp_colorTexture = std::move(v);
+ colorTexture = cpp_colorTexture.c_str();
+ }
+
+ private:
+ std::filesystem::path cpp_colorTexture {};
+ };
+
+ struct LightInfo : remixapi_LightInfo {
+ LightInfo() {
+ sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO;
+ pNext = nullptr;
+ hash = 0;
+ radiance = { 1.0f, 1.0f, 1.0f };
+ static_assert(sizeof remixapi_LightInfo == 40);
+ }
+ };
+
+ inline Result< remixapi_LightHandle > Interface::CreateLight(const LightInfo& info) {
+ remixapi_LightHandle handle = nullptr;
+ remixapi_ErrorCode status = m_CInterface.CreateLight(&info, &handle);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return handle;
+ }
+
+ inline Result< void > Interface::DestroyLight(remixapi_LightHandle handle) {
+ return m_CInterface.DestroyLight(handle);
+ }
+
+ inline Result< void > Interface::DrawLightInstance(remixapi_LightHandle handle) {
+ return m_CInterface.DrawLightInstance(handle);
+ }
+
+ namespace detail {
+ struct dxvk_ExternalSwapchain {
+ uint64_t vkImage;
+ uint64_t vkSemaphoreRenderingDone;
+ uint64_t vkSemaphoreResumeSemaphore;
+ };
+
+ struct dxvk_VkImage {
+ uint64_t vkImage;
+ };
+ }
+
+ inline Result< IDirect3D9Ex* > Interface::dxvk_CreateD3D9(bool disableSrgbConversionForOutput) {
+ IDirect3D9Ex* d3d9 { nullptr };
+ remixapi_ErrorCode status = m_CInterface.dxvk_CreateD3D9(disableSrgbConversionForOutput, &d3d9);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return d3d9;
+ }
+
+ inline Result< void > Interface::dxvk_RegisterD3D9Device(IDirect3DDevice9Ex* d3d9Device) {
+ return m_CInterface.dxvk_RegisterD3D9Device(d3d9Device);
+ }
+
+ inline Result< detail::dxvk_ExternalSwapchain > Interface::dxvk_GetExternalSwapchain() {
+ detail::dxvk_ExternalSwapchain externalSwapchain {};
+ remixapi_ErrorCode status = m_CInterface.dxvk_GetExternalSwapchain(
+ &externalSwapchain.vkImage,
+ &externalSwapchain.vkSemaphoreRenderingDone,
+ &externalSwapchain.vkSemaphoreResumeSemaphore);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return externalSwapchain;
+ }
+
+ inline Result< detail::dxvk_VkImage > Interface::dxvk_GetVkImage(IDirect3DSurface9* source) {
+ detail::dxvk_VkImage externalImage {};
+ remixapi_ErrorCode status = m_CInterface.dxvk_GetVkImage(source, &externalImage.vkImage);
+ if (status != REMIXAPI_ERROR_CODE_SUCCESS) {
+ return status;
+ }
+ return externalImage;
+ }
+
+ inline Result< void > Interface::dxvk_CopyRenderingOutput(
+ IDirect3DSurface9* destination, remixapi_dxvk_CopyRenderingOutputType type) {
+ return m_CInterface.dxvk_CopyRenderingOutput(destination, type);
+ }
+}
diff --git a/public/include/remix/remix_c.h b/public/include/remix/remix_c.h
new file mode 100644
index 000000000..f5b586203
--- /dev/null
+++ b/public/include/remix/remix_c.h
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef REMIX_C_H_
+#define REMIX_C_H_
+
+#include
+#include
+
+
+// __stdcall convention
+#define REMIXAPI_CALL __stdcall
+#define REMIXAPI_PTR REMIXAPI_CALL
+
+#ifdef REMIX_LIBRARY_EXPORTS
+ #define REMIXAPI __declspec(dllexport)
+#else
+ #define REMIXAPI __declspec(dllimport)
+#endif // REMIX_LIBRARY_EXPORTS
+
+
+#define REMIXAPI_VERSION_MAKE(major, minor, patch) ( \
+ (((uint64_t)(major)) << 48) | \
+ (((uint64_t)(minor)) << 16) | \
+ (((uint64_t)(patch)) ) )
+#define REMIXAPI_VERSION_GET_MAJOR(version) (((uint64_t)(version) >> 48) & (uint64_t)0xFFFF)
+#define REMIXAPI_VERSION_GET_MINOR(version) (((uint64_t)(version) >> 16) & (uint64_t)0xFFFFFFFF)
+#define REMIXAPI_VERSION_GET_PATCH(version) (((uint64_t)(version) ) & (uint64_t)0xFFFF)
+
+#define REMIXAPI_VERSION_MAJOR 0
+#define REMIXAPI_VERSION_MINOR 2
+#define REMIXAPI_VERSION_PATCH 0
+
+
+// External
+typedef struct IDirect3D9Ex IDirect3D9Ex;
+typedef struct IDirect3DDevice9Ex IDirect3DDevice9Ex;
+typedef struct IDirect3DSurface9 IDirect3DSurface9;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+ typedef enum remixapi_StructType {
+ REMIXAPI_STRUCT_TYPE_NONE = 0,
+ REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO = 1,
+ REMIXAPI_STRUCT_TYPE_MATERIAL_INFO = 2,
+ REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT = 3,
+ REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT = 4,
+ REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT = 5,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO = 6,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT = 7,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT = 8,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT = 9,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT = 10,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT = 11,
+ REMIXAPI_STRUCT_TYPE_MESH_INFO = 12,
+ REMIXAPI_STRUCT_TYPE_INSTANCE_INFO = 13,
+ REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT = 14,
+ REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT = 15,
+ REMIXAPI_STRUCT_TYPE_CAMERA_INFO = 16,
+ REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT = 17,
+ REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT = 18,
+ REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT = 19,
+ REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT = 20,
+ } remixapi_StructType;
+
+ typedef enum remixapi_ErrorCode {
+ REMIXAPI_ERROR_CODE_SUCCESS = 0,
+ REMIXAPI_ERROR_CODE_GENERAL_FAILURE = 1,
+ // WinAPI's LoadLibrary has failed
+ REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE = 2,
+ REMIXAPI_ERROR_CODE_WRONG_ARGUMENTS = 3,
+ // Couldn't find 'remixInitialize' function in the .dll
+ REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE = 4,
+ // CreateD3D9 / RegisterD3D9Device can be called only once
+ REMIXAPI_ERROR_CODE_ALREADY_EXISTS = 5,
+ // RegisterD3D9Device requires the device that was created with IDirect3DDevice9Ex, returned by CreateD3D9
+ REMIXAPI_ERROR_CODE_REGISTERING_NON_REMIX_D3D9_DEVICE = 6,
+ // RegisterD3D9Device was not called
+ REMIXAPI_ERROR_CODE_REMIX_DEVICE_WAS_NOT_REGISTERED = 7,
+ REMIXAPI_ERROR_CODE_INCOMPATIBLE_VERSION = 8,
+ } remixapi_ErrorCode;
+
+ typedef uint32_t remixapi_Bool;
+
+ typedef struct remixapi_Float2D {
+ float x;
+ float y;
+ float z;
+ } remixapi_Float2D;
+
+ typedef struct remixapi_Float3D {
+ float x;
+ float y;
+ float z;
+ } remixapi_Float3D;
+
+ typedef struct remixapi_Float4D {
+ float x;
+ float y;
+ float z;
+ float w;
+ } remixapi_Float4D;
+
+ typedef struct remixapi_Transform {
+ float matrix[3][4];
+ } remixapi_Transform;
+
+ typedef struct remixapi_MaterialHandle_T* remixapi_MaterialHandle;
+ typedef struct remixapi_MeshHandle_T* remixapi_MeshHandle;
+ typedef struct remixapi_LightHandle_T* remixapi_LightHandle;
+
+ typedef const wchar_t* remixapi_Path;
+
+
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_Shutdown)();
+
+
+
+ typedef struct remixapi_MaterialInfoOpaqueEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Path roughnessTexture;
+ remixapi_Path metallicTexture;
+ float anisotropy;
+ remixapi_Float3D albedoConstant;
+ float opacityConstant;
+ float roughnessConstant;
+ float metallicConstant;
+ remixapi_Bool thinFilmThickness_hasvalue;
+ float thinFilmThickness_value;
+ remixapi_Bool alphaIsThinFilmThickness;
+ remixapi_Path heightTexture;
+ float heightTextureStrength;
+ // If true, InstanceInfoBlendEXT is used as a source for alpha state
+ remixapi_Bool useDrawCallAlphaState;
+ remixapi_Bool blendType_hasvalue;
+ int blendType_value;
+ remixapi_Bool invertedBlend;
+ int alphaTestType;
+ uint8_t alphaReferenceValue;
+ } remixapi_MaterialInfoOpaqueEXT;
+
+ // Valid only if remixapi_MaterialInfo contains remixapi_MaterialInfoOpaqueEXT in pNext chain
+ typedef struct remixapi_MaterialInfoOpaqueSubsurfaceEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Path subsurfaceTransmittanceTexture;
+ remixapi_Path subsurfaceThicknessTexture;
+ remixapi_Path subsurfaceSingleScatteringAlbedoTexture;
+ remixapi_Float3D subsurfaceTransmittanceColor;
+ float subsurfaceMeasurementDistance;
+ remixapi_Float3D subsurfaceSingleScatteringAlbedo;
+ float subsurfaceVolumetricAnisotropy;
+ } remixapi_MaterialInfoOpaqueSubsurfaceEXT;
+
+ typedef struct remixapi_MaterialInfoTranslucentEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Path transmittanceTexture;
+ float refractiveIndex;
+ remixapi_Float3D transmittanceColor;
+ float transmittanceMeasurementDistance;
+ remixapi_Bool thinWallThickness_hasvalue;
+ float thinWallThickness_value;
+ remixapi_Bool useDiffuseLayer;
+ } remixapi_MaterialInfoTranslucentEXT;
+
+ typedef struct remixapi_MaterialInfoPortalEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ uint8_t rayPortalIndex;
+ float rotationSpeed;
+ } remixapi_MaterialInfoPortalEXT;
+
+ typedef struct remixapi_MaterialInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ uint64_t hash;
+ remixapi_Path albedoTexture;
+ remixapi_Path normalTexture;
+ remixapi_Path tangentTexture;
+ remixapi_Path emissiveTexture;
+ float emissiveIntensity;
+ remixapi_Float3D emissiveColorConstant;
+ uint8_t spriteSheetRow;
+ uint8_t spriteSheetCol;
+ uint8_t spriteSheetFps;
+ uint8_t filterMode;
+ uint8_t wrapModeU;
+ uint8_t wrapModeV;
+ } remixapi_MaterialInfo;
+
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateMaterial)(
+ const remixapi_MaterialInfo* info,
+ remixapi_MaterialHandle* out_handle);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyMaterial)(
+ remixapi_MaterialHandle handle);
+
+ typedef struct remixapi_HardcodedVertex {
+ float position[3];
+ float normal[3];
+ float texcoord[2];
+ uint32_t color;
+ uint32_t _pad0;
+ uint32_t _pad1;
+ uint32_t _pad2;
+ uint32_t _pad3;
+ uint32_t _pad4;
+ uint32_t _pad5;
+ uint32_t _pad6;
+ } remixapi_HardcodedVertex;
+
+ typedef struct remixapi_MeshInfoSkinning {
+ uint32_t bonesPerVertex;
+ // Each tuple of 'bonesPerVertex' float-s defines a vertex.
+ // I.e. the size must be (bonesPerVertex * vertexCount).
+ const float* blendWeights_values;
+ uint32_t blendWeights_count;
+ // Each tuple of 'bonesPerVertex' uint32_t-s defines a vertex.
+ // I.e. the size must be (bonesPerVertex * vertexCount).
+ const uint32_t* blendIndices_values;
+ uint32_t blendIndices_count;
+ } remixapi_MeshInfoSkinning;
+
+ typedef struct remixapi_MeshInfoSurfaceTriangles {
+ const remixapi_HardcodedVertex* vertices_values;
+ uint64_t vertices_count;
+ const uint32_t* indices_values;
+ uint64_t indices_count;
+ remixapi_Bool skinning_hasvalue;
+ remixapi_MeshInfoSkinning skinning_value;
+ remixapi_MaterialHandle material;
+ } remixapi_MeshInfoSurfaceTriangles;
+
+ typedef struct remixapi_MeshInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ uint64_t hash;
+ const remixapi_MeshInfoSurfaceTriangles* surfaces_values;
+ uint32_t surfaces_count;
+ } remixapi_MeshInfo;
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateMesh)(
+ const remixapi_MeshInfo* info,
+ remixapi_MeshHandle* out_handle);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyMesh)(
+ remixapi_MeshHandle handle);
+
+
+
+ typedef enum remixapi_CameraType {
+ REMIXAPI_CAMERA_TYPE_WORLD,
+ REMIXAPI_CAMERA_TYPE_SKY,
+ REMIXAPI_CAMERA_TYPE_VIEW_MODEL,
+ } remixapi_CameraType;
+
+ typedef struct remixapi_CameraInfoParameterizedEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D position;
+ remixapi_Float3D forward;
+ remixapi_Float3D up;
+ remixapi_Float3D right;
+ float fovYInDegrees;
+ float aspect;
+ float nearPlane;
+ float farPlane;
+ } remixapi_CameraInfoParameterizedEXT;
+
+ typedef struct remixapi_CameraInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_CameraType type;
+ float view[4][4];
+ float projection[4][4];
+ } remixapi_CameraInfo;
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_SetupCamera)(
+ const remixapi_CameraInfo* info);
+
+
+
+#define REMIXAPI_INSTANCE_INFO_MAX_BONES_COUNT 256
+
+ typedef struct remixapi_InstanceInfoBoneTransformsEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ const remixapi_Transform* boneTransforms_values;
+ uint32_t boneTransforms_count;
+ } remixapi_InstanceInfoBoneTransformsEXT;
+
+ typedef struct remixapi_InstanceInfoBlendEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Bool alphaTestEnabled;
+ uint8_t alphaTestReferenceValue;
+ uint32_t alphaTestCompareOp;
+ remixapi_Bool alphaBlendEnabled;
+ uint32_t srcColorBlendFactor;
+ uint32_t dstColorBlendFactor;
+ uint32_t colorBlendOp;
+ uint32_t textureColorArg1Source;
+ uint32_t textureColorArg2Source;
+ uint32_t textureColorOperation;
+ uint32_t textureAlphaArg1Source;
+ uint32_t textureAlphaArg2Source;
+ uint32_t textureAlphaOperation;
+ uint32_t tFactor;
+ remixapi_Bool isTextureFactorBlend;
+ } remixapi_InstanceInfoBlendEXT;
+
+ typedef struct remixapi_InstanceInfoObjectPickingEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ // A value to write into REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_OBJECT_PICKING
+ uint32_t objectPickingValue;
+ } remixapi_InstanceInfoObjectPickingEXT;
+
+ typedef enum remixapi_InstanceCategoryBit {
+ REMIXAPI_INSTANCE_CATEGORY_BIT_WORLD_UI = 1 << 0,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_WORLD_MATTE = 1 << 1,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_SKY = 1 << 2,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE = 1 << 3,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_LIGHTS = 1 << 4,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_ANTI_CULLING = 1 << 5,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_MOTION_BLUR = 1 << 6,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_OPACITY_MICROMAP = 1 << 7,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_HIDDEN = 1 << 8,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_PARTICLE = 1 << 9,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_BEAM = 1 << 10,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_STATIC = 1 << 11,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_DYNAMIC = 1 << 12,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_SINGLE_OFFSET = 1 << 13,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_NO_OFFSET = 1 << 14,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_ALPHA_BLEND_TO_CUTOUT = 1 << 15,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_TERRAIN = 1 << 16,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_ANIMATED_WATER = 1 << 17,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_MODEL = 1 << 18,
+ REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_BODY = 1 << 19,
+ } remixapi_InstanceCategoryBit;
+
+ typedef uint32_t remixapi_InstanceCategoryFlags;
+
+ typedef struct remixapi_InstanceInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_InstanceCategoryFlags categoryFlags;
+ remixapi_MeshHandle mesh;
+ remixapi_Transform transform;
+ remixapi_Bool doubleSided;
+ } remixapi_InstanceInfo;
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DrawInstance)(
+ const remixapi_InstanceInfo* info);
+
+
+
+ typedef struct remixapi_LightInfoLightShaping {
+ remixapi_Float3D primaryAxis;
+ float coneAngleDegrees;
+ float coneSoftness;
+ float focusExponent;
+ } remixapi_LightInfoLightShaping;
+
+ typedef struct remixapi_LightInfoSphereEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D position;
+ float radius;
+ remixapi_Bool shaping_hasvalue;
+ remixapi_LightInfoLightShaping shaping_value;
+ } remixapi_LightInfoSphereEXT;
+
+ typedef struct remixapi_LightInfoRectEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D position;
+ remixapi_Float3D xAxis;
+ float xSize;
+ remixapi_Float3D yAxis;
+ float ySize;
+ remixapi_Bool shaping_hasvalue;
+ remixapi_LightInfoLightShaping shaping_value;
+ } remixapi_LightInfoRectEXT;
+
+ typedef struct remixapi_LightInfoDiskEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D position;
+ remixapi_Float3D xAxis;
+ float xRadius;
+ remixapi_Float3D yAxis;
+ float yRadius;
+ remixapi_Bool shaping_hasvalue;
+ remixapi_LightInfoLightShaping shaping_value;
+ } remixapi_LightInfoDiskEXT;
+
+ typedef struct remixapi_LightInfoCylinderEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D position;
+ float radius;
+ remixapi_Float3D axis;
+ float axisLength;
+ } remixapi_LightInfoCylinderEXT;
+
+ typedef struct remixapi_LightInfoDistantEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Float3D direction;
+ float angularDiameterDegrees;
+ } remixapi_LightInfoDistantEXT;
+
+ typedef struct remixapi_LightInfoDomeEXT {
+ remixapi_StructType sType;
+ void* pNext;
+ remixapi_Transform transform;
+ remixapi_Path colorTexture;
+ } remixapi_LightInfoDomeEXT;
+
+ typedef struct remixapi_LightInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ uint64_t hash;
+ remixapi_Float3D radiance;
+ } remixapi_LightInfo;
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateLight)(
+ const remixapi_LightInfo* info,
+ remixapi_LightHandle* out_handle);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyLight)(
+ remixapi_LightHandle handle);
+
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DrawLightInstance)(
+ remixapi_LightHandle lightHandle);
+
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_SetConfigVariable)(
+ const char* key,
+ const char* value);
+
+
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_CreateD3D9)(
+ remixapi_Bool disableSrgbConversionForOutput,
+ IDirect3D9Ex** out_pD3D9);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_RegisterD3D9Device)(
+ IDirect3DDevice9Ex* d3d9Device);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_GetExternalSwapchain)(
+ uint64_t* out_vkImage,
+ uint64_t* out_vkSemaphoreRenderingDone,
+ uint64_t* out_vkSemaphoreResumeSemaphore);
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_GetVkImage)(
+ IDirect3DSurface9* source,
+ uint64_t* out_vkImage);
+
+ typedef enum remixapi_dxvk_CopyRenderingOutputType {
+ REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_FINAL_COLOR = 0,
+ REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_DEPTH = 1,
+ REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_NORMALS = 2,
+ REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_OBJECT_PICKING = 3,
+ } remixapi_dxvk_CopyRenderingOutputType;
+
+ typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_CopyRenderingOutput)(
+ IDirect3DSurface9* destination,
+ remixapi_dxvk_CopyRenderingOutputType type);
+
+
+
+ typedef struct remixapi_InitializeLibraryInfo {
+ remixapi_StructType sType;
+ void* pNext;
+ uint64_t version;
+ } remixapi_InitializeLibraryInfo;
+
+ typedef struct remixapi_Interface {
+ PFN_remixapi_Shutdown Shutdown;
+ PFN_remixapi_CreateMaterial CreateMaterial;
+ PFN_remixapi_DestroyMaterial DestroyMaterial;
+ PFN_remixapi_CreateMesh CreateMesh;
+ PFN_remixapi_DestroyMesh DestroyMesh;
+ PFN_remixapi_SetupCamera SetupCamera;
+ PFN_remixapi_DrawInstance DrawInstance;
+ PFN_remixapi_CreateLight CreateLight;
+ PFN_remixapi_DestroyLight DestroyLight;
+ PFN_remixapi_DrawLightInstance DrawLightInstance;
+ PFN_remixapi_SetConfigVariable SetConfigVariable;
+ // DXVK interoperability
+ PFN_remixapi_dxvk_CreateD3D9 dxvk_CreateD3D9;
+ PFN_remixapi_dxvk_RegisterD3D9Device dxvk_RegisterD3D9Device;
+ PFN_remixapi_dxvk_GetExternalSwapchain dxvk_GetExternalSwapchain;
+ PFN_remixapi_dxvk_GetVkImage dxvk_GetVkImage;
+ PFN_remixapi_dxvk_CopyRenderingOutput dxvk_CopyRenderingOutput;
+ } remixapi_Interface;
+
+ REMIXAPI remixapi_ErrorCode REMIXAPI_CALL remixapi_InitializeLibrary(
+ const remixapi_InitializeLibraryInfo* info,
+ remixapi_Interface* out_result);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // REMIX_C_H_
diff --git a/src/d3d11/d3d11.def b/src/d3d11/d3d11.def
index 8b65655a0..d406ba0b3 100644
--- a/src/d3d11/d3d11.def
+++ b/src/d3d11/d3d11.def
@@ -3,3 +3,4 @@ EXPORTS
D3D11CoreCreateDevice @18
D3D11CreateDevice @22
D3D11CreateDeviceAndSwapChain @23
+ D3D11On12CreateDevice @24
diff --git a/src/d3d11/d3d11_buffer.cpp b/src/d3d11/d3d11_buffer.cpp
index 687be0a51..9b1ee35aa 100644
--- a/src/d3d11/d3d11_buffer.cpp
+++ b/src/d3d11/d3d11_buffer.cpp
@@ -74,6 +74,8 @@ namespace dxvk {
m_buffer = m_parent->GetDXVKDevice()->createBuffer(info, GetMemoryFlags());
m_mapped = m_buffer->getSliceHandle();
+ m_mapMode = DetermineMapMode();
+
// For Stream Output buffers we need a counter
if (pDesc->BindFlags & D3D11_BIND_STREAM_OUTPUT)
m_soCounter = CreateSoCounterBuffer();
@@ -273,6 +275,13 @@ namespace dxvk {
| VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
return device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
+
+
+ D3D11_COMMON_BUFFER_MAP_MODE D3D11Buffer::DetermineMapMode() {
+ return (m_buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ ? D3D11_COMMON_BUFFER_MAP_MODE_DIRECT
+ : D3D11_COMMON_BUFFER_MAP_MODE_NONE;
+ }
D3D11Buffer* GetCommonBuffer(ID3D11Resource* pResource) {
diff --git a/src/d3d11/d3d11_buffer.h b/src/d3d11/d3d11_buffer.h
index 3ddd61a0a..c0472fd65 100644
--- a/src/d3d11/d3d11_buffer.h
+++ b/src/d3d11/d3d11_buffer.h
@@ -1,5 +1,6 @@
#pragma once
+#include "../dxvk/dxvk_cs.h"
#include "../dxvk/dxvk_device.h"
#include "../d3d10/d3d10_buffer.h"
@@ -67,9 +68,7 @@ namespace dxvk {
}
D3D11_COMMON_BUFFER_MAP_MODE GetMapMode() const {
- return (m_buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
- ? D3D11_COMMON_BUFFER_MAP_MODE_DIRECT
- : D3D11_COMMON_BUFFER_MAP_MODE_NONE;
+ return m_mapMode;
}
Rc GetBuffer() const {
@@ -119,6 +118,21 @@ namespace dxvk {
return &m_d3d10;
}
+ bool HasSequenceNumber() const {
+ return m_mapMode != D3D11_COMMON_BUFFER_MAP_MODE_NONE
+ && !(m_desc.MiscFlags & D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS)
+ && !(m_desc.BindFlags);
+ }
+
+ void TrackSequenceNumber(uint64_t Seq) {
+ m_seq = Seq;
+ }
+
+ uint64_t GetSequenceNumber() {
+ return HasSequenceNumber() ? m_seq
+ : DxvkCsThread::SynchronizeAll;
+ }
+
/**
* \brief Normalizes buffer description
*
@@ -130,14 +144,16 @@ namespace dxvk {
private:
- const D3D11_BUFFER_DESC m_desc;
+ D3D11_BUFFER_DESC m_desc;
+ D3D11_COMMON_BUFFER_MAP_MODE m_mapMode;
- Rc m_buffer;
- Rc m_soCounter;
- DxvkBufferSliceHandle m_mapped;
+ Rc m_buffer;
+ Rc m_soCounter;
+ DxvkBufferSliceHandle m_mapped;
+ uint64_t m_seq = 0ull;
- D3D11DXGIResource m_resource;
- D3D10Buffer m_d3d10;
+ D3D11DXGIResource m_resource;
+ D3D10Buffer m_d3d10;
BOOL CheckFormatFeatureSupport(
VkFormat Format,
@@ -147,6 +163,8 @@ namespace dxvk {
Rc CreateSoCounterBuffer();
+ D3D11_COMMON_BUFFER_MAP_MODE DetermineMapMode();
+
};
diff --git a/src/d3d11/d3d11_cmdlist.cpp b/src/d3d11/d3d11_cmdlist.cpp
index 0ce56211a..aeb1cd5a2 100644
--- a/src/d3d11/d3d11_cmdlist.cpp
+++ b/src/d3d11/d3d11_cmdlist.cpp
@@ -1,5 +1,7 @@
#include "d3d11_cmdlist.h"
#include "d3d11_device.h"
+#include "d3d11_buffer.h"
+#include "d3d11_texture.h"
namespace dxvk {
@@ -58,21 +60,71 @@ namespace dxvk {
for (const auto& query : m_queries)
cmdList->m_queries.push_back(query);
+ for (const auto& resource : m_resources)
+ cmdList->m_resources.push_back(resource);
+
MarkSubmitted();
}
- void D3D11CommandList::EmitToCsThread(DxvkCsThread* CsThread) {
+ uint64_t D3D11CommandList::EmitToCsThread(DxvkCsThread* CsThread) {
+ uint64_t seq = 0;
+
for (const auto& query : m_queries)
query->DoDeferredEnd();
for (const auto& chunk : m_chunks)
- CsThread->dispatchChunk(DxvkCsChunkRef(chunk));
+ seq = CsThread->dispatchChunk(DxvkCsChunkRef(chunk));
+ for (const auto& resource : m_resources)
+ TrackResourceSequenceNumber(resource, seq);
+
MarkSubmitted();
+ return seq;
}
+ void D3D11CommandList::TrackResourceUsage(
+ ID3D11Resource* pResource,
+ D3D11_RESOURCE_DIMENSION ResourceType,
+ UINT Subresource) {
+ m_resources.emplace_back(pResource, Subresource, ResourceType);
+ }
+
+
+ void D3D11CommandList::TrackResourceSequenceNumber(
+ const D3D11ResourceRef& Resource,
+ uint64_t Seq) {
+ ID3D11Resource* iface = Resource.Get();
+ UINT subresource = Resource.GetSubresource();
+
+ switch (Resource.GetType()) {
+ case D3D11_RESOURCE_DIMENSION_UNKNOWN:
+ break;
+
+ case D3D11_RESOURCE_DIMENSION_BUFFER: {
+ auto impl = static_cast(iface);
+ impl->TrackSequenceNumber(Seq);
+ } break;
+
+ case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {
+ auto impl = static_cast(iface)->GetCommonTexture();
+ impl->TrackSequenceNumber(subresource, Seq);
+ } break;
+
+ case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {
+ auto impl = static_cast(iface)->GetCommonTexture();
+ impl->TrackSequenceNumber(subresource, Seq);
+ } break;
+
+ case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {
+ auto impl = static_cast(iface)->GetCommonTexture();
+ impl->TrackSequenceNumber(subresource, Seq);
+ } break;
+ }
+ }
+
+
void D3D11CommandList::MarkSubmitted() {
if (m_submitted.exchange(true) && !m_warned.exchange(true)
&& m_parent->GetOptions()->dcSingleUseMode) {
diff --git a/src/d3d11/d3d11_cmdlist.h b/src/d3d11/d3d11_cmdlist.h
index ce207aaf4..8be313ad6 100644
--- a/src/d3d11/d3d11_cmdlist.h
+++ b/src/d3d11/d3d11_cmdlist.h
@@ -29,19 +29,29 @@ namespace dxvk {
void EmitToCommandList(
ID3D11CommandList* pCommandList);
- void EmitToCsThread(
+ uint64_t EmitToCsThread(
DxvkCsThread* CsThread);
-
+
+ void TrackResourceUsage(
+ ID3D11Resource* pResource,
+ D3D11_RESOURCE_DIMENSION ResourceType,
+ UINT Subresource);
+
private:
-
+
UINT const m_contextFlags;
std::vector m_chunks;
std::vector> m_queries;
+ std::vector m_resources;
std::atomic m_submitted = { false };
std::atomic m_warned = { false };
+ void TrackResourceSequenceNumber(
+ const D3D11ResourceRef& Resource,
+ uint64_t Seq);
+
void MarkSubmitted();
};
diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp
index 45af2f7bd..cc470eee2 100644
--- a/src/d3d11/d3d11_context.cpp
+++ b/src/d3d11/d3d11_context.cpp
@@ -473,6 +473,9 @@ namespace dxvk {
cSrcSlice.offset(),
sizeof(uint32_t));
});
+
+ if (buf->HasSequenceNumber())
+ TrackBufferSequenceNumber(buf);
}
@@ -903,134 +906,6 @@ namespace dxvk {
});
}
-
- void STDMETHODCALLTYPE D3D11DeviceContext::UpdateSubresource(
- ID3D11Resource* pDstResource,
- UINT DstSubresource,
- const D3D11_BOX* pDstBox,
- const void* pSrcData,
- UINT SrcRowPitch,
- UINT SrcDepthPitch) {
- UpdateSubresource1(pDstResource,
- DstSubresource, pDstBox, pSrcData,
- SrcRowPitch, SrcDepthPitch, 0);
- }
-
-
- void STDMETHODCALLTYPE D3D11DeviceContext::UpdateSubresource1(
- ID3D11Resource* pDstResource,
- UINT DstSubresource,
- const D3D11_BOX* pDstBox,
- const void* pSrcData,
- UINT SrcRowPitch,
- UINT SrcDepthPitch,
- UINT CopyFlags) {
- D3D10DeviceLock lock = LockContext();
-
- if (!pDstResource)
- return;
-
- // Filter out invalid copy flags
- CopyFlags &= D3D11_COPY_NO_OVERWRITE | D3D11_COPY_DISCARD;
-
- // We need a different code path for buffers
- D3D11_RESOURCE_DIMENSION resourceType;
- pDstResource->GetType(&resourceType);
-
- if (resourceType == D3D11_RESOURCE_DIMENSION_BUFFER) {
- const auto bufferResource = static_cast(pDstResource);
- const auto bufferSlice = bufferResource->GetBufferSlice();
-
- VkDeviceSize offset = bufferSlice.offset();
- VkDeviceSize size = bufferSlice.length();
-
- if (pDstBox != nullptr) {
- offset = pDstBox->left;
- size = pDstBox->right - pDstBox->left;
- }
-
- if (!size || offset + size > bufferSlice.length())
- return;
-
- bool useMap = (bufferSlice.buffer()->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
- && (size == bufferSlice.length() || CopyFlags);
-
- if (useMap) {
- D3D11_MAP mapType = (CopyFlags & D3D11_COPY_NO_OVERWRITE)
- ? D3D11_MAP_WRITE_NO_OVERWRITE
- : D3D11_MAP_WRITE_DISCARD;
-
- D3D11_MAPPED_SUBRESOURCE mappedSr;
- if (likely(useMap = SUCCEEDED(Map(pDstResource, 0, mapType, 0, &mappedSr)))) {
- std::memcpy(reinterpret_cast(mappedSr.pData) + offset, pSrcData, size);
- Unmap(pDstResource, 0);
- }
- }
-
- if (!useMap) {
- DxvkDataSlice dataSlice = AllocUpdateBufferSlice(size);
- std::memcpy(dataSlice.ptr(), pSrcData, size);
-
- EmitCs([
- cDataBuffer = std::move(dataSlice),
- cBufferSlice = bufferSlice.subSlice(offset, size)
- ] (DxvkContext* ctx) {
- ctx->updateBuffer(
- cBufferSlice.buffer(),
- cBufferSlice.offset(),
- cBufferSlice.length(),
- cDataBuffer.ptr());
- });
- }
- } else {
- D3D11CommonTexture* dstTexture = GetCommonTexture(pDstResource);
-
- if (DstSubresource >= dstTexture->CountSubresources())
- return;
-
- VkFormat packedFormat = dstTexture->GetPackedFormat();
-
- auto formatInfo = imageFormatInfo(packedFormat);
- auto subresource = dstTexture->GetSubresourceFromIndex(
- formatInfo->aspectMask, DstSubresource);
-
- VkExtent3D mipExtent = dstTexture->MipLevelExtent(subresource.mipLevel);
-
- VkOffset3D offset = { 0, 0, 0 };
- VkExtent3D extent = mipExtent;
-
- if (pDstBox != nullptr) {
- if (pDstBox->left >= pDstBox->right
- || pDstBox->top >= pDstBox->bottom
- || pDstBox->front >= pDstBox->back)
- return; // no-op, but legal
-
- offset.x = pDstBox->left;
- offset.y = pDstBox->top;
- offset.z = pDstBox->front;
-
- extent.width = pDstBox->right - pDstBox->left;
- extent.height = pDstBox->bottom - pDstBox->top;
- extent.depth = pDstBox->back - pDstBox->front;
- }
-
- if (!util::isBlockAligned(offset, extent, formatInfo->blockSize, mipExtent)) {
- Logger::err("D3D11: UpdateSubresource1: Unaligned region");
- return;
- }
-
- auto stagingSlice = AllocStagingBuffer(util::computeImageDataSize(packedFormat, extent));
-
- util::packImageData(stagingSlice.mapPtr(0),
- pSrcData, SrcRowPitch, SrcDepthPitch, 0, 0,
- dstTexture->GetVkImageType(), extent, 1,
- formatInfo, formatInfo->aspectMask);
-
- UpdateImage(dstTexture, &subresource,
- offset, extent, std::move(stagingSlice));
- }
- }
-
HRESULT STDMETHODCALLTYPE D3D11DeviceContext::UpdateTileMappings(
ID3D11Resource* pTiledResource,
@@ -1131,8 +1006,8 @@ namespace dxvk {
return;
}
- const D3D11CommonTexture* dstTextureInfo = GetCommonTexture(pDstResource);
- const D3D11CommonTexture* srcTextureInfo = GetCommonTexture(pSrcResource);
+ D3D11CommonTexture* dstTextureInfo = GetCommonTexture(pDstResource);
+ D3D11CommonTexture* srcTextureInfo = GetCommonTexture(pSrcResource);
const DXGI_VK_FORMAT_INFO dstFormatInfo = m_parent->LookupFormat(dstDesc.Format, DXGI_VK_FORMAT_MODE_ANY);
const DXGI_VK_FORMAT_INFO srcFormatInfo = m_parent->LookupFormat(srcDesc.Format, DXGI_VK_FORMAT_MODE_ANY);
@@ -1195,6 +1070,9 @@ namespace dxvk {
ctx->resolveImage(cDstImage, cSrcImage, region, cFormat);
});
}
+
+ if (dstTextureInfo->HasSequenceNumber())
+ TrackTextureSequenceNumber(dstTextureInfo, DstSubresource);
}
@@ -3404,6 +3282,11 @@ namespace dxvk {
cSrcBuffer.length());
}
});
+
+ if (pDstBuffer->HasSequenceNumber())
+ TrackBufferSequenceNumber(pDstBuffer);
+ if (pSrcBuffer->HasSequenceNumber())
+ TrackBufferSequenceNumber(pSrcBuffer);
}
@@ -3638,6 +3521,20 @@ namespace dxvk {
}
}
}
+
+ if (pDstTexture->HasSequenceNumber()) {
+ for (uint32_t i = 0; i < pDstLayers->layerCount; i++) {
+ TrackTextureSequenceNumber(pDstTexture, D3D11CalcSubresource(
+ pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, pDstTexture->Desc()->MipLevels));
+ }
+ }
+
+ if (pSrcTexture->HasSequenceNumber()) {
+ for (uint32_t i = 0; i < pSrcLayers->layerCount; i++) {
+ TrackTextureSequenceNumber(pSrcTexture, D3D11CalcSubresource(
+ pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, pSrcTexture->Desc()->MipLevels));
+ }
+ }
}
@@ -3668,6 +3565,106 @@ namespace dxvk {
}
+ void D3D11DeviceContext::UpdateBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData) {
+ DxvkBufferSlice bufferSlice = pDstBuffer->GetBufferSlice(Offset, Length);
+
+ if (Length <= 1024 && !(Offset & 0x3) && !(Length & 0x3)) {
+ // The backend has special code paths for small buffer updates,
+ // however both offset and size must be aligned to four bytes.
+ DxvkDataSlice dataSlice = AllocUpdateBufferSlice(Length);
+ std::memcpy(dataSlice.ptr(), pSrcData, Length);
+
+ EmitCs([
+ cDataBuffer = std::move(dataSlice),
+ cBufferSlice = std::move(bufferSlice)
+ ] (DxvkContext* ctx) {
+ ctx->updateBuffer(
+ cBufferSlice.buffer(),
+ cBufferSlice.offset(),
+ cBufferSlice.length(),
+ cDataBuffer.ptr());
+ });
+ } else {
+ // Otherwise, to avoid large data copies on the CS thread,
+ // write directly to a staging buffer and dispatch a copy
+ DxvkBufferSlice stagingSlice = AllocStagingBuffer(Length);
+ std::memcpy(stagingSlice.mapPtr(0), pSrcData, Length);
+
+ EmitCs([
+ cStagingSlice = std::move(stagingSlice),
+ cBufferSlice = std::move(bufferSlice)
+ ] (DxvkContext* ctx) {
+ ctx->copyBuffer(
+ cBufferSlice.buffer(),
+ cBufferSlice.offset(),
+ cStagingSlice.buffer(),
+ cStagingSlice.offset(),
+ cBufferSlice.length());
+ });
+ }
+
+ if (pDstBuffer->HasSequenceNumber())
+ TrackBufferSequenceNumber(pDstBuffer);
+ }
+
+
+ void D3D11DeviceContext::UpdateTexture(
+ D3D11CommonTexture* pDstTexture,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch) {
+ if (DstSubresource >= pDstTexture->CountSubresources())
+ return;
+
+ VkFormat packedFormat = pDstTexture->GetPackedFormat();
+
+ auto formatInfo = imageFormatInfo(packedFormat);
+ auto subresource = pDstTexture->GetSubresourceFromIndex(
+ formatInfo->aspectMask, DstSubresource);
+
+ VkExtent3D mipExtent = pDstTexture->MipLevelExtent(subresource.mipLevel);
+
+ VkOffset3D offset = { 0, 0, 0 };
+ VkExtent3D extent = mipExtent;
+
+ if (pDstBox != nullptr) {
+ if (pDstBox->left >= pDstBox->right
+ || pDstBox->top >= pDstBox->bottom
+ || pDstBox->front >= pDstBox->back)
+ return; // no-op, but legal
+
+ offset.x = pDstBox->left;
+ offset.y = pDstBox->top;
+ offset.z = pDstBox->front;
+
+ extent.width = pDstBox->right - pDstBox->left;
+ extent.height = pDstBox->bottom - pDstBox->top;
+ extent.depth = pDstBox->back - pDstBox->front;
+ }
+
+ if (!util::isBlockAligned(offset, extent, formatInfo->blockSize, mipExtent)) {
+ Logger::err("D3D11: UpdateSubresource1: Unaligned region");
+ return;
+ }
+
+ auto stagingSlice = AllocStagingBuffer(util::computeImageDataSize(packedFormat, extent));
+
+ util::packImageData(stagingSlice.mapPtr(0),
+ pSrcData, SrcRowPitch, SrcDepthPitch, 0, 0,
+ pDstTexture->GetVkImageType(), extent, 1,
+ formatInfo, formatInfo->aspectMask);
+
+ UpdateImage(pDstTexture, &subresource,
+ offset, extent, std::move(stagingSlice));
+ }
+
+
void D3D11DeviceContext::UpdateImage(
D3D11CommonTexture* pDstTexture,
const VkImageSubresource* pDstSubresource,
@@ -3676,6 +3673,9 @@ namespace dxvk {
DxvkBufferSlice StagingBuffer) {
bool dstIsImage = pDstTexture->GetMapMode() != D3D11_COMMON_TEXTURE_MAP_MODE_STAGING;
+ uint32_t dstSubresource = D3D11CalcSubresource(pDstSubresource->mipLevel,
+ pDstSubresource->arrayLayer, pDstTexture->Desc()->MipLevels);
+
if (dstIsImage) {
EmitCs([
cDstImage = pDstTexture->GetImage(),
@@ -3707,9 +3707,6 @@ namespace dxvk {
// format metadata, so deal with it manually here.
VkExtent3D dstMipExtent = pDstTexture->MipLevelExtent(pDstSubresource->mipLevel);
- uint32_t dstSubresource = D3D11CalcSubresource(pDstSubresource->mipLevel,
- pDstSubresource->arrayLayer, pDstTexture->Desc()->MipLevels);
-
auto dstFormat = pDstTexture->GetPackedFormat();
auto dstFormatInfo = imageFormatInfo(dstFormat);
@@ -3757,6 +3754,9 @@ namespace dxvk {
srcPlaneOffset += util::flattenImageExtent(blockCount) * elementSize;
}
}
+
+ if (pDstTexture->HasSequenceNumber())
+ TrackTextureSequenceNumber(pDstTexture, dstSubresource);
}
@@ -4398,7 +4398,7 @@ namespace dxvk {
DxvkDataSlice D3D11DeviceContext::AllocUpdateBufferSlice(size_t Size) {
- constexpr size_t UpdateBufferSize = 16 * 1024 * 1024;
+ constexpr size_t UpdateBufferSize = 1 * 1024 * 1024;
if (Size >= UpdateBufferSize) {
Rc buffer = new DxvkDataBuffer(Size);
@@ -4421,6 +4421,8 @@ namespace dxvk {
DxvkBufferSlice D3D11DeviceContext::AllocStagingBuffer(
VkDeviceSize Size) {
+ constexpr VkDeviceSize StagingBufferSize = 4 * 1024 * 1024;
+
DxvkBufferCreateInfo info;
info.size = Size;
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
@@ -4432,8 +4434,32 @@ namespace dxvk {
info.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_SHADER_READ_BIT;
- return DxvkBufferSlice(m_device->createBuffer(info,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));
+ // Create a dedicated buffer for large allocations
+ VkDeviceSize alignedSize = align(Size, 256);
+
+ if (alignedSize >= StagingBufferSize) {
+ return DxvkBufferSlice(m_device->createBuffer(info,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));
+ }
+
+ // Otherwise, try to suballocate from an existing buffer
+ if (m_stagingOffset + alignedSize > StagingBufferSize || m_stagingBuffer == nullptr) {
+ info.size = StagingBufferSize;
+
+ m_stagingBuffer = m_device->createBuffer(info,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+ m_stagingOffset = 0;
+ }
+
+ DxvkBufferSlice slice(m_stagingBuffer, m_stagingOffset, Size);
+ m_stagingOffset += alignedSize;
+ return slice;
+ }
+
+
+ void D3D11DeviceContext::ResetStagingBuffer() {
+ m_stagingBuffer = nullptr;
+ m_stagingOffset = 0;
}
@@ -4504,4 +4530,25 @@ namespace dxvk {
pMsState->enableAlphaToCoverage = VK_FALSE;
}
+
+ void D3D11DeviceContext::TrackResourceSequenceNumber(
+ ID3D11Resource* pResource) {
+ if (!pResource)
+ return;
+
+ D3D11CommonTexture* texture = GetCommonTexture(pResource);
+
+ if (texture) {
+ if (texture->HasSequenceNumber()) {
+ for (uint32_t i = 0; i < texture->CountSubresources(); i++)
+ TrackTextureSequenceNumber(texture, i);
+ }
+ } else {
+ D3D11Buffer* buffer = static_cast(pResource);
+
+ if (buffer->HasSequenceNumber())
+ TrackBufferSequenceNumber(buffer);
+ }
+ }
+
}
diff --git a/src/d3d11/d3d11_context.h b/src/d3d11/d3d11_context.h
index 483b04157..48b84427d 100644
--- a/src/d3d11/d3d11_context.h
+++ b/src/d3d11/d3d11_context.h
@@ -133,23 +133,6 @@ namespace dxvk {
void STDMETHODCALLTYPE GenerateMips(
ID3D11ShaderResourceView* pShaderResourceView);
- void STDMETHODCALLTYPE UpdateSubresource(
- ID3D11Resource* pDstResource,
- UINT DstSubresource,
- const D3D11_BOX* pDstBox,
- const void* pSrcData,
- UINT SrcRowPitch,
- UINT SrcDepthPitch);
-
- void STDMETHODCALLTYPE UpdateSubresource1(
- ID3D11Resource* pDstResource,
- UINT DstSubresource,
- const D3D11_BOX* pDstBox,
- const void* pSrcData,
- UINT SrcRowPitch,
- UINT SrcDepthPitch,
- UINT CopyFlags);
-
HRESULT STDMETHODCALLTYPE UpdateTileMappings(
ID3D11Resource* pTiledResource,
UINT NumTiledResourceRegions,
@@ -712,7 +695,10 @@ namespace dxvk {
Rc m_device;
Rc m_updateBuffer;
-
+
+ Rc m_stagingBuffer;
+ VkDeviceSize m_stagingOffset = 0ull;
+
DxvkCsChunkFlags m_csFlags;
DxvkCsChunkRef m_csChunk;
@@ -804,6 +790,82 @@ namespace dxvk {
ID3D11Resource* pResource,
UINT Subresource);
+ template
+ static void UpdateResource(
+ ContextType* pContext,
+ ID3D11Resource* pDstResource,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch,
+ UINT CopyFlags) {
+ D3D10DeviceLock lock = pContext->LockContext();
+
+ if (!pDstResource)
+ return;
+
+ // We need a different code path for buffers
+ D3D11_RESOURCE_DIMENSION resourceType;
+ pDstResource->GetType(&resourceType);
+
+ if (likely(resourceType == D3D11_RESOURCE_DIMENSION_BUFFER)) {
+ const auto bufferResource = static_cast(pDstResource);
+ uint64_t bufferSize = bufferResource->Desc()->ByteWidth;
+
+ // Provide a fast path for mapped buffer updates since some
+ // games use UpdateSubresource to update constant buffers.
+ if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT) && likely(!pDstBox)) {
+ pContext->UpdateMappedBuffer(bufferResource, 0, bufferSize, pSrcData, 0);
+ return;
+ }
+
+ // Validate buffer range to update
+ uint64_t offset = 0;
+ uint64_t length = bufferSize;
+
+ if (pDstBox) {
+ offset = pDstBox->left;
+ length = pDstBox->right - offset;
+ }
+
+ if (unlikely(offset + length > bufferSize))
+ return;
+
+ // Still try to be fast if a box is provided but we update the full buffer
+ if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT)) {
+ CopyFlags &= D3D11_COPY_DISCARD | D3D11_COPY_NO_OVERWRITE;
+
+ if (likely(length == bufferSize) || unlikely(CopyFlags != 0)) {
+ pContext->UpdateMappedBuffer(bufferResource, offset, length, pSrcData, CopyFlags);
+ return;
+ }
+ }
+
+ // Otherwise we can't really do anything fancy, so just do a GPU copy
+ pContext->UpdateBuffer(bufferResource, offset, length, pSrcData);
+ } else {
+ D3D11CommonTexture* textureResource = GetCommonTexture(pDstResource);
+
+ pContext->UpdateTexture(textureResource,
+ DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch);
+ }
+ }
+
+ void UpdateBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData);
+
+ void UpdateTexture(
+ D3D11CommonTexture* pDstTexture,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch);
+
void UpdateImage(
D3D11CommonTexture* pDstTexture,
const VkImageSubresource* pDstSubresource,
@@ -918,7 +980,7 @@ namespace dxvk {
UINT NumViews,
ID3D11RenderTargetView* const* ppRenderTargetViews,
ID3D11DepthStencilView* pDepthStencilView);
-
+
VkClearValue ConvertColorValue(
const FLOAT Color[4],
const DxvkFormatInfo* pFormatInfo);
@@ -927,7 +989,9 @@ namespace dxvk {
DxvkBufferSlice AllocStagingBuffer(
VkDeviceSize Size);
-
+
+ void ResetStagingBuffer();
+
DxvkCsChunkRef AllocCsChunk();
static void InitDefaultPrimitiveTopology(
@@ -1004,8 +1068,18 @@ namespace dxvk {
}
}
+ void TrackResourceSequenceNumber(
+ ID3D11Resource* pResource);
+
virtual void EmitCsChunk(DxvkCsChunkRef&& chunk) = 0;
+ virtual void TrackTextureSequenceNumber(
+ D3D11CommonTexture* pResource,
+ UINT Subresource) = 0;
+
+ virtual void TrackBufferSequenceNumber(
+ D3D11Buffer* pResource) = 0;
+
};
}
diff --git a/src/d3d11/d3d11_context_def.cpp b/src/d3d11/d3d11_context_def.cpp
index 1ac44b061..552cfcc81 100644
--- a/src/d3d11/d3d11_context_def.cpp
+++ b/src/d3d11/d3d11_context_def.cpp
@@ -175,6 +175,7 @@ namespace dxvk {
ClearState();
m_mappedResources.clear();
+ ResetStagingBuffer();
return S_OK;
}
@@ -190,47 +191,35 @@ namespace dxvk {
if (unlikely(!pResource || !pMappedResource))
return E_INVALIDARG;
- D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
- pResource->GetType(&resourceDim);
-
if (MapType == D3D11_MAP_WRITE_DISCARD) {
- D3D11DeferredContextMapEntry entry;
-
+ D3D11_RESOURCE_DIMENSION resourceDim;
+ pResource->GetType(&resourceDim);
+
+ D3D11_MAPPED_SUBRESOURCE mapInfo;
HRESULT status = resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER
- ? MapBuffer(pResource, MapType, MapFlags, &entry)
- : MapImage (pResource, Subresource, MapType, MapFlags, &entry);
+ ? MapBuffer(pResource, &mapInfo)
+ : MapImage (pResource, Subresource, &mapInfo);
if (unlikely(FAILED(status))) {
*pMappedResource = D3D11_MAPPED_SUBRESOURCE();
return status;
}
- // Adding a new map entry actually overrides the
- // old one in practice because the lookup function
- // scans the array in reverse order
- m_mappedResources.push_back(std::move(entry));
-
- // Fill mapped resource structure
- pMappedResource->pData = entry.MapPointer;
- pMappedResource->RowPitch = entry.RowPitch;
- pMappedResource->DepthPitch = entry.DepthPitch;
+ AddMapEntry(pResource, Subresource, resourceDim, mapInfo);
+ *pMappedResource = mapInfo;
return S_OK;
} else if (MapType == D3D11_MAP_WRITE_NO_OVERWRITE) {
// The resource must be mapped with D3D11_MAP_WRITE_DISCARD
// before it can be mapped with D3D11_MAP_WRITE_NO_OVERWRITE.
auto entry = FindMapEntry(pResource, Subresource);
- if (unlikely(entry == m_mappedResources.rend())) {
+ if (unlikely(!entry)) {
*pMappedResource = D3D11_MAPPED_SUBRESOURCE();
return E_INVALIDARG;
}
// Return same memory region as earlier
- entry->MapType = D3D11_MAP_WRITE_NO_OVERWRITE;
-
- pMappedResource->pData = entry->MapPointer;
- pMappedResource->RowPitch = entry->RowPitch;
- pMappedResource->DepthPitch = entry->DepthPitch;
+ *pMappedResource = entry->MapInfo;
return S_OK;
} else {
// Not allowed on deferred contexts
@@ -247,6 +236,31 @@ namespace dxvk {
}
+ void STDMETHODCALLTYPE D3D11DeferredContext::UpdateSubresource(
+ ID3D11Resource* pDstResource,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch) {
+ UpdateResource(this, pDstResource,
+ DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, 0);
+ }
+
+
+ void STDMETHODCALLTYPE D3D11DeferredContext::UpdateSubresource1(
+ ID3D11Resource* pDstResource,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch,
+ UINT CopyFlags) {
+ UpdateResource(this, pDstResource,
+ DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, CopyFlags);
+ }
+
+
void STDMETHODCALLTYPE D3D11DeferredContext::SwapDeviceContextState(
ID3DDeviceContextState* pState,
ID3DDeviceContextState** ppPreviousState) {
@@ -259,9 +273,7 @@ namespace dxvk {
HRESULT D3D11DeferredContext::MapBuffer(
ID3D11Resource* pResource,
- D3D11_MAP MapType,
- UINT MapFlags,
- D3D11DeferredContextMapEntry* pMapEntry) {
+ D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
D3D11Buffer* pBuffer = static_cast(pResource);
if (unlikely(pBuffer->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) {
@@ -269,18 +281,15 @@ namespace dxvk {
return E_INVALIDARG;
}
- pMapEntry->pResource = pResource;
- pMapEntry->Subresource = 0;
- pMapEntry->MapType = D3D11_MAP_WRITE_DISCARD;
- pMapEntry->RowPitch = pBuffer->Desc()->ByteWidth;
- pMapEntry->DepthPitch = pBuffer->Desc()->ByteWidth;
+ pMappedResource->RowPitch = pBuffer->Desc()->ByteWidth;
+ pMappedResource->DepthPitch = pBuffer->Desc()->ByteWidth;
if (likely(m_csFlags.test(DxvkCsChunkFlag::SingleUse))) {
// For resources that cannot be written by the GPU,
// we may write to the buffer resource directly and
// just swap in the buffer slice as needed.
auto bufferSlice = pBuffer->AllocSlice();
- pMapEntry->MapPointer = bufferSlice.mapPtr;
+ pMappedResource->pData = bufferSlice.mapPtr;
EmitCs([
cDstBuffer = pBuffer->GetBuffer(),
@@ -292,7 +301,7 @@ namespace dxvk {
// For GPU-writable resources, we need a data slice
// to perform the update operation at execution time.
auto dataSlice = AllocUpdateBufferSlice(pBuffer->Desc()->ByteWidth);
- pMapEntry->MapPointer = dataSlice.ptr();
+ pMappedResource->pData = dataSlice.ptr();
EmitCs([
cDstBuffer = pBuffer->GetBuffer(),
@@ -311,9 +320,7 @@ namespace dxvk {
HRESULT D3D11DeferredContext::MapImage(
ID3D11Resource* pResource,
UINT Subresource,
- D3D11_MAP MapType,
- UINT MapFlags,
- D3D11DeferredContextMapEntry* pMapEntry) {
+ D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
D3D11CommonTexture* pTexture = GetCommonTexture(pResource);
if (unlikely(pTexture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE)) {
@@ -335,12 +342,9 @@ namespace dxvk {
auto layout = pTexture->GetSubresourceLayout(formatInfo->aspectMask, Subresource);
auto dataSlice = AllocStagingBuffer(util::computeImageDataSize(packedFormat, levelExtent));
- pMapEntry->pResource = pResource;
- pMapEntry->Subresource = Subresource;
- pMapEntry->MapType = D3D11_MAP_WRITE_DISCARD;
- pMapEntry->RowPitch = layout.RowPitch;
- pMapEntry->DepthPitch = layout.DepthPitch;
- pMapEntry->MapPointer = dataSlice.mapPtr(0);
+ pMappedResource->RowPitch = layout.RowPitch;
+ pMappedResource->DepthPitch = layout.DepthPitch;
+ pMappedResource->pData = dataSlice.mapPtr(0);
UpdateImage(pTexture, &subresource,
VkOffset3D { 0, 0, 0 }, levelExtent,
@@ -349,6 +353,34 @@ namespace dxvk {
}
+ void D3D11DeferredContext::UpdateMappedBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData,
+ UINT CopyFlags) {
+ void* mapPtr = nullptr;
+
+ if (unlikely(CopyFlags == D3D11_COPY_NO_OVERWRITE)) {
+ auto entry = FindMapEntry(pDstBuffer, 0);
+
+ if (entry)
+ mapPtr = entry->MapInfo.pData;
+ }
+
+ if (likely(!mapPtr)) {
+ // The caller validates the map mode, so we can
+ // safely ignore the MapBuffer return value here
+ D3D11_MAPPED_SUBRESOURCE mapInfo;
+ MapBuffer(pDstBuffer, &mapInfo);
+ AddMapEntry(pDstBuffer, 0, D3D11_RESOURCE_DIMENSION_BUFFER, mapInfo);
+ mapPtr = mapInfo.pData;
+ }
+
+ std::memcpy(reinterpret_cast(mapPtr) + Offset, pSrcData, Length);
+ }
+
+
void D3D11DeferredContext::FinalizeQueries() {
for (auto& query : m_queriesBegun) {
m_commandList->AddQuery(query.ptr());
@@ -373,6 +405,52 @@ namespace dxvk {
}
+ void D3D11DeferredContext::TrackTextureSequenceNumber(
+ D3D11CommonTexture* pResource,
+ UINT Subresource) {
+ m_commandList->TrackResourceUsage(
+ pResource->GetInterface(),
+ pResource->GetDimension(),
+ Subresource);
+ }
+
+
+ void D3D11DeferredContext::TrackBufferSequenceNumber(
+ D3D11Buffer* pResource) {
+ m_commandList->TrackResourceUsage(
+ pResource, D3D11_RESOURCE_DIMENSION_BUFFER, 0);
+ }
+
+
+ D3D11DeferredContextMapEntry* D3D11DeferredContext::FindMapEntry(
+ ID3D11Resource* pResource,
+ UINT Subresource) {
+ // Recently mapped resources as well as entries with
+ // up-to-date map infos will be located at the end
+ // of the resource array, so scan in reverse order.
+ size_t size = m_mappedResources.size();
+
+ for (size_t i = 1; i <= size; i++) {
+ auto entry = &m_mappedResources[size - i];
+
+ if (entry->Resource.Get() == pResource
+ && entry->Resource.GetSubresource() == Subresource)
+ return entry;
+ }
+
+ return nullptr;
+ }
+
+ void D3D11DeferredContext::AddMapEntry(
+ ID3D11Resource* pResource,
+ UINT Subresource,
+ D3D11_RESOURCE_DIMENSION ResourceType,
+ const D3D11_MAPPED_SUBRESOURCE& MapInfo) {
+ m_mappedResources.emplace_back(pResource,
+ Subresource, ResourceType, MapInfo);
+ }
+
+
DxvkCsChunkFlags D3D11DeferredContext::GetCsChunkFlags(
D3D11Device* pDevice) {
return pDevice->GetOptions()->dcSingleUseMode
diff --git a/src/d3d11/d3d11_context_def.h b/src/d3d11/d3d11_context_def.h
index bdfcf4412..c5fe49c9d 100644
--- a/src/d3d11/d3d11_context_def.h
+++ b/src/d3d11/d3d11_context_def.h
@@ -5,22 +5,26 @@
#include "d3d11_context.h"
#include "d3d11_texture.h"
-#include
#include
namespace dxvk {
struct D3D11DeferredContextMapEntry {
- Com pResource;
- UINT Subresource;
- D3D11_MAP MapType;
- UINT RowPitch;
- UINT DepthPitch;
- void* MapPointer;
+ D3D11DeferredContextMapEntry() { }
+ D3D11DeferredContextMapEntry(
+ ID3D11Resource* pResource,
+ UINT Subresource,
+ D3D11_RESOURCE_DIMENSION ResourceType,
+ const D3D11_MAPPED_SUBRESOURCE& MappedResource)
+ : Resource(pResource, Subresource, ResourceType),
+ MapInfo(MappedResource) { }
+
+ D3D11ResourceRef Resource;
+ D3D11_MAPPED_SUBRESOURCE MapInfo;
};
class D3D11DeferredContext : public D3D11DeviceContext {
-
+ friend class D3D11DeviceContext;
public:
D3D11DeferredContext(
@@ -77,6 +81,23 @@ namespace dxvk {
ID3D11Resource* pResource,
UINT Subresource);
+ void STDMETHODCALLTYPE UpdateSubresource(
+ ID3D11Resource* pDstResource,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch);
+
+ void STDMETHODCALLTYPE UpdateSubresource1(
+ ID3D11Resource* pDstResource,
+ UINT DstSubresource,
+ const D3D11_BOX* pDstBox,
+ const void* pSrcData,
+ UINT SrcRowPitch,
+ UINT SrcDepthPitch,
+ UINT CopyFlags);
+
void STDMETHODCALLTYPE SwapDeviceContextState(
ID3DDeviceContextState* pState,
ID3DDeviceContextState** ppPreviousState);
@@ -98,34 +119,46 @@ namespace dxvk {
HRESULT MapBuffer(
ID3D11Resource* pResource,
- D3D11_MAP MapType,
- UINT MapFlags,
- D3D11DeferredContextMapEntry* pMapEntry);
+ D3D11_MAPPED_SUBRESOURCE* pMappedResource);
HRESULT MapImage(
ID3D11Resource* pResource,
UINT Subresource,
- D3D11_MAP MapType,
- UINT MapFlags,
- D3D11DeferredContextMapEntry* pMapEntry);
-
+ D3D11_MAPPED_SUBRESOURCE* pMappedResource);
+
+ void UpdateMappedBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData,
+ UINT CopyFlags);
+
void FinalizeQueries();
Com CreateCommandList();
void EmitCsChunk(DxvkCsChunkRef&& chunk);
+ void TrackTextureSequenceNumber(
+ D3D11CommonTexture* pResource,
+ UINT Subresource);
+
+ void TrackBufferSequenceNumber(
+ D3D11Buffer* pResource);
+
+ D3D11DeferredContextMapEntry* FindMapEntry(
+ ID3D11Resource* pResource,
+ UINT Subresource);
+
+ void AddMapEntry(
+ ID3D11Resource* pResource,
+ UINT Subresource,
+ D3D11_RESOURCE_DIMENSION ResourceType,
+ const D3D11_MAPPED_SUBRESOURCE& MapInfo);
+
static DxvkCsChunkFlags GetCsChunkFlags(
D3D11Device* pDevice);
- auto FindMapEntry(ID3D11Resource* pResource, UINT Subresource) {
- return std::find_if(m_mappedResources.rbegin(), m_mappedResources.rend(),
- [pResource, Subresource] (const D3D11DeferredContextMapEntry& entry) {
- return entry.pResource == pResource
- && entry.Subresource == Subresource;
- });
- }
-
};
}
diff --git a/src/d3d11/d3d11_context_ext.cpp b/src/d3d11/d3d11_context_ext.cpp
index c892f7efd..ecd41e832 100644
--- a/src/d3d11/d3d11_context_ext.cpp
+++ b/src/d3d11/d3d11_context_ext.cpp
@@ -193,6 +193,13 @@ namespace dxvk {
ctx->launchCuKernelNVX(cLaunchInfo.nvxLaunchInfo, cLaunchInfo.buffers, cLaunchInfo.images);
});
+ // Track resource usage as necessary
+ for (uint32_t i = 0; i < NumReadResources; i++)
+ m_ctx->TrackResourceSequenceNumber(static_cast(pReadResources[i]));
+
+ for (uint32_t i = 0; i < NumWriteResources; i++)
+ m_ctx->TrackResourceSequenceNumber(static_cast(pWriteResources[i]));
+
return true;
}
}
diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp
index adaf28105..0523cc5f1 100644
--- a/src/d3d11/d3d11_context_imm.cpp
+++ b/src/d3d11/d3d11_context_imm.cpp
@@ -7,13 +7,15 @@ constexpr static uint32_t MinFlushIntervalUs = 750;
constexpr static uint32_t IncFlushIntervalUs = 250;
constexpr static uint32_t MaxPendingSubmits = 6;
+constexpr static VkDeviceSize MaxImplicitDiscardSize = 256ull << 10;
+
namespace dxvk {
D3D11ImmediateContext::D3D11ImmediateContext(
D3D11Device* pParent,
const Rc& Device)
: D3D11DeviceContext(pParent, Device, DxvkCsChunkFlag::SingleUse),
- m_csThread(Device->createContext()),
+ m_csThread(Device, Device->createContext()),
m_videoContext(this, Device) {
EmitCs([
cDevice = m_device,
@@ -39,7 +41,7 @@ namespace dxvk {
D3D11ImmediateContext::~D3D11ImmediateContext() {
Flush();
- SynchronizeCsThread();
+ SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
SynchronizeDevice();
}
@@ -212,7 +214,8 @@ namespace dxvk {
// Dispatch command list to the CS thread and
// restore the immediate context's state
- commandList->EmitToCsThread(&m_csThread);
+ uint64_t csSeqNum = commandList->EmitToCsThread(&m_csThread);
+ m_csSeqNum = std::max(m_csSeqNum, csSeqNum);
if (RestoreContextState)
RestoreState();
@@ -272,12 +275,17 @@ namespace dxvk {
void STDMETHODCALLTYPE D3D11ImmediateContext::Unmap(
ID3D11Resource* pResource,
UINT Subresource) {
- D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
- pResource->GetType(&resourceDim);
-
- if (unlikely(resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER)) {
- D3D10DeviceLock lock = LockContext();
- UnmapImage(GetCommonTexture(pResource), Subresource);
+ // Since it is very uncommon for images to be mapped compared
+ // to buffers, we count the currently mapped images in order
+ // to avoid a virtual method call in the common case.
+ if (unlikely(m_mappedImageCount > 0)) {
+ D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
+ pResource->GetType(&resourceDim);
+
+ if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) {
+ D3D10DeviceLock lock = LockContext();
+ UnmapImage(GetCommonTexture(pResource), Subresource);
+ }
}
}
@@ -288,11 +296,8 @@ namespace dxvk {
const void* pSrcData,
UINT SrcRowPitch,
UINT SrcDepthPitch) {
- FlushImplicit(FALSE);
-
- D3D11DeviceContext::UpdateSubresource(
- pDstResource, DstSubresource, pDstBox,
- pSrcData, SrcRowPitch, SrcDepthPitch);
+ UpdateResource(this, pDstResource,
+ DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, 0);
}
@@ -304,12 +309,8 @@ namespace dxvk {
UINT SrcRowPitch,
UINT SrcDepthPitch,
UINT CopyFlags) {
- FlushImplicit(FALSE);
-
- D3D11DeviceContext::UpdateSubresource1(
- pDstResource, DstSubresource, pDstBox,
- pSrcData, SrcRowPitch, SrcDepthPitch,
- CopyFlags);
+ UpdateResource(this, pDstResource,
+ DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, CopyFlags);
}
@@ -353,15 +354,17 @@ namespace dxvk {
Logger::err("D3D11: Cannot map a device-local buffer");
return E_INVALIDARG;
}
-
- if (MapType == D3D11_MAP_WRITE_DISCARD) {
+
+ VkDeviceSize bufferSize = pResource->Desc()->ByteWidth;
+
+ if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) {
// Allocate a new backing slice for the buffer and set
// it as the 'new' mapped slice. This assumes that the
// only way to invalidate a buffer is by mapping it.
auto physSlice = pResource->DiscardSlice();
pMappedResource->pData = physSlice.mapPtr;
- pMappedResource->RowPitch = pResource->Desc()->ByteWidth;
- pMappedResource->DepthPitch = pResource->Desc()->ByteWidth;
+ pMappedResource->RowPitch = bufferSize;
+ pMappedResource->DepthPitch = bufferSize;
EmitCs([
cBuffer = pResource->GetBuffer(),
@@ -371,22 +374,65 @@ namespace dxvk {
});
return S_OK;
- } else {
- // Wait until the resource is no longer in use
- if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
- if (!WaitForResource(pResource->GetBuffer(), MapType, MapFlags))
- return DXGI_ERROR_WAS_STILL_DRAWING;
- }
-
- // Use map pointer from previous map operation. This
- // way we don't have to synchronize with the CS thread
- // if the map mode is D3D11_MAP_WRITE_NO_OVERWRITE.
+ } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) {
+ // Put this on a fast path without any extra checks since it's
+ // a somewhat desired method to partially update large buffers
DxvkBufferSliceHandle physSlice = pResource->GetMappedSlice();
-
pMappedResource->pData = physSlice.mapPtr;
- pMappedResource->RowPitch = pResource->Desc()->ByteWidth;
- pMappedResource->DepthPitch = pResource->Desc()->ByteWidth;
+ pMappedResource->RowPitch = bufferSize;
+ pMappedResource->DepthPitch = bufferSize;
return S_OK;
+ } else {
+ // Quantum Break likes using MAP_WRITE on resources which would force
+ // us to synchronize with the GPU multiple times per frame. In those
+ // situations, if there are no pending GPU writes to the resource, we
+ // can promote it to MAP_WRITE_DISCARD, but preserve the data by doing
+ // a CPU copy from the previous buffer slice, to avoid the sync point.
+ bool doInvalidatePreserve = false;
+
+ auto buffer = pResource->GetBuffer();
+ auto sequenceNumber = pResource->GetSequenceNumber();
+
+ if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= MaxImplicitDiscardSize) {
+ SynchronizeCsThread(sequenceNumber);
+
+ bool hasWoAccess = buffer->isInUse(DxvkAccess::Write);
+ bool hasRwAccess = buffer->isInUse(DxvkAccess::Read);
+
+ if (hasRwAccess && !hasWoAccess) {
+ // Uncached reads can be so slow that a GPU sync may actually be faster
+ doInvalidatePreserve = buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ }
+ }
+
+ if (doInvalidatePreserve) {
+ FlushImplicit(TRUE);
+
+ auto prevSlice = pResource->GetMappedSlice();
+ auto physSlice = pResource->DiscardSlice();
+
+ EmitCs([
+ cBuffer = std::move(buffer),
+ cBufferSlice = physSlice
+ ] (DxvkContext* ctx) {
+ ctx->invalidateBuffer(cBuffer, cBufferSlice);
+ });
+
+ std::memcpy(physSlice.mapPtr, prevSlice.mapPtr, physSlice.length);
+ pMappedResource->pData = physSlice.mapPtr;
+ pMappedResource->RowPitch = bufferSize;
+ pMappedResource->DepthPitch = bufferSize;
+ return S_OK;
+ } else {
+ if (!WaitForResource(buffer, sequenceNumber, MapType, MapFlags))
+ return DXGI_ERROR_WAS_STILL_DRAWING;
+
+ DxvkBufferSliceHandle physSlice = pResource->GetMappedSlice();
+ pMappedResource->pData = physSlice.mapPtr;
+ pMappedResource->RowPitch = bufferSize;
+ pMappedResource->DepthPitch = bufferSize;
+ return S_OK;
+ }
}
}
@@ -423,23 +469,83 @@ namespace dxvk {
VkFormat packedFormat = m_parent->LookupPackedFormat(
pResource->Desc()->Format, pResource->GetFormatMode()).Format;
+ uint64_t sequenceNumber = pResource->GetSequenceNumber(Subresource);
+
auto formatInfo = imageFormatInfo(packedFormat);
void* mapPtr;
if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {
- // Wait for the resource to become available
- if (!WaitForResource(mappedImage, MapType, MapFlags))
- return DXGI_ERROR_WAS_STILL_DRAWING;
+ // Wait for the resource to become available. We do not
+ // support image renaming, so stall on DISCARD instead.
+ if (MapType == D3D11_MAP_WRITE_DISCARD)
+ MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT;
+
+ if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
+ if (!WaitForResource(mappedImage, sequenceNumber, MapType, MapFlags))
+ return DXGI_ERROR_WAS_STILL_DRAWING;
+ }
// Query the subresource's memory layout and hope that
// the application respects the returned pitch values.
mapPtr = mappedImage->mapPtr(0);
} else {
- if (MapType == D3D11_MAP_WRITE_DISCARD) {
- // We do not have to preserve the contents of the
- // buffer if the entire image gets discarded.
+ constexpr uint32_t DoInvalidate = (1u << 0);
+ constexpr uint32_t DoPreserve = (1u << 1);
+ constexpr uint32_t DoWait = (1u << 2);
+ uint32_t doFlags;
+
+ if (MapType == D3D11_MAP_READ) {
+ // Reads will not change the image content, so we only need
+ // to wait for the GPU to finish writing to the mapped buffer.
+ doFlags = DoWait;
+ } else if (MapType == D3D11_MAP_WRITE_DISCARD) {
+ doFlags = DoInvalidate;
+
+ // If we know for sure that the mapped buffer is currently not
+ // in use by the GPU, we don't have to allocate a new slice.
+ if (m_csThread.lastSequenceNumber() >= sequenceNumber && !mappedBuffer->isInUse(DxvkAccess::Read))
+ doFlags = 0;
+ } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING && (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)) {
+ // Always respect DO_NOT_WAIT for mapped staging images
+ doFlags = DoWait;
+ } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {
+ // Need to synchronize thread to determine pending GPU accesses
+ SynchronizeCsThread(sequenceNumber);
+
+ // Don't implicitly discard large buffers or buffers of images with
+ // multiple subresources, as that is likely to cause memory issues.
+ VkDeviceSize bufferSize = pResource->CountSubresources() == 1
+ ? pResource->GetMappedSlice(Subresource).length
+ : MaxImplicitDiscardSize;
+
+ if (bufferSize >= MaxImplicitDiscardSize) {
+ // Don't check access flags, WaitForResource will return
+ // early anyway if the resource is currently in use
+ doFlags = DoWait;
+ } else if (mappedBuffer->isInUse(DxvkAccess::Write)) {
+ // There are pending GPU writes, need to wait for those
+ doFlags = DoWait;
+ } else if (mappedBuffer->isInUse(DxvkAccess::Read)) {
+ // All pending GPU accesses are reads, so the buffer data
+ // is still current, and we can prevent GPU synchronization
+ // by creating a new slice with an exact copy of the data.
+ doFlags = DoInvalidate | DoPreserve;
+ } else {
+ // There are no pending accesses, so we don't need to wait
+ doFlags = 0;
+ }
+ } else {
+ // No need to synchronize staging resources with NO_OVERWRITE
+ // since the buffer will be used directly.
+ doFlags = 0;
+ }
+
+ if (doFlags & DoInvalidate) {
+ FlushImplicit(TRUE);
+
+ DxvkBufferSliceHandle prevSlice = pResource->GetMappedSlice(Subresource);
DxvkBufferSliceHandle physSlice = pResource->DiscardSlice(Subresource);
-
+
EmitCs([
cImageBuffer = mappedBuffer,
cBufferSlice = physSlice
@@ -447,15 +553,22 @@ namespace dxvk {
ctx->invalidateBuffer(cImageBuffer, cBufferSlice);
});
+ if (doFlags & DoPreserve)
+ std::memcpy(physSlice.mapPtr, prevSlice.mapPtr, physSlice.length);
+
mapPtr = physSlice.mapPtr;
} else {
- bool wait = MapType != D3D11_MAP_WRITE_NO_OVERWRITE
- || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER;
-
- // Wait for mapped buffer to become available
- if (wait && !WaitForResource(mappedBuffer, MapType, MapFlags))
- return DXGI_ERROR_WAS_STILL_DRAWING;
-
+ if (doFlags & DoWait) {
+ // We cannot respect DO_NOT_WAIT for buffer-mapped resources since
+ // our internal copies need to be transparent to the application.
+ if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)
+ MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT;
+
+ // Wait for mapped buffer to become available
+ if (!WaitForResource(mappedBuffer, sequenceNumber, MapType, MapFlags))
+ return DXGI_ERROR_WAS_STILL_DRAWING;
+ }
+
mapPtr = pResource->GetMappedSlice(Subresource).mapPtr;
}
}
@@ -470,6 +583,7 @@ namespace dxvk {
pMappedResource->DepthPitch = layout.DepthPitch;
}
+ m_mappedImageCount += 1;
return S_OK;
}
@@ -480,11 +594,15 @@ namespace dxvk {
D3D11_MAP mapType = pResource->GetMapType(Subresource);
pResource->SetMapType(Subresource, D3D11_MAP(~0u));
- if (mapType == D3D11_MAP(~0u)
- || mapType == D3D11_MAP_READ)
+ if (mapType == D3D11_MAP(~0u))
return;
-
- if (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {
+
+ // Decrement mapped image counter only after making sure
+ // the given subresource is actually mapped right now
+ m_mappedImageCount -= 1;
+
+ if ((mapType != D3D11_MAP_READ) &&
+ (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)) {
// Now that data has been written into the buffer,
// we need to copy its contents into the image
VkImageAspectFlags aspectMask = imageFormatInfo(pResource->GetPackedFormat())->aspectMask;
@@ -497,6 +615,31 @@ namespace dxvk {
}
+ void D3D11ImmediateContext::UpdateMappedBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData,
+ UINT CopyFlags) {
+ DxvkBufferSliceHandle slice;
+
+ if (likely(CopyFlags != D3D11_COPY_NO_OVERWRITE)) {
+ slice = pDstBuffer->DiscardSlice();
+
+ EmitCs([
+ cBuffer = pDstBuffer->GetBuffer(),
+ cBufferSlice = slice
+ ] (DxvkContext* ctx) {
+ ctx->invalidateBuffer(cBuffer, cBufferSlice);
+ });
+ } else {
+ slice = pDstBuffer->GetMappedSlice();
+ }
+
+ std::memcpy(reinterpret_cast(slice.mapPtr) + Offset, pSrcData, Length);
+ }
+
+
void STDMETHODCALLTYPE D3D11ImmediateContext::SwapDeviceContextState(
ID3DDeviceContextState* pState,
ID3DDeviceContextState** ppPreviousState) {
@@ -523,15 +666,15 @@ namespace dxvk {
}
- void D3D11ImmediateContext::SynchronizeCsThread() {
+ void D3D11ImmediateContext::SynchronizeCsThread(uint64_t SequenceNumber) {
D3D10DeviceLock lock = LockContext();
// Dispatch current chunk so that all commands
// recorded prior to this function will be run
- FlushCsChunk();
+ if (SequenceNumber > m_csSeqNum)
+ FlushCsChunk();
- if (m_csThread.isBusy())
- m_csThread.synchronize();
+ m_csThread.synchronize(SequenceNumber);
}
@@ -542,6 +685,7 @@ namespace dxvk {
bool D3D11ImmediateContext::WaitForResource(
const Rc& Resource,
+ uint64_t SequenceNumber,
D3D11_MAP MapType,
UINT MapFlags) {
// Determine access type to wait for based on map mode
@@ -549,39 +693,58 @@ namespace dxvk {
? DxvkAccess::Write
: DxvkAccess::Read;
- // Wait for the any pending D3D11 command to be executed
- // on the CS thread so that we can determine whether the
- // resource is currently in use or not.
- if (!Resource->isInUse(access))
- SynchronizeCsThread();
-
- if (Resource->isInUse(access)) {
- if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) {
+ // Wait for any CS chunk using the resource to execute, since
+ // otherwise we cannot accurately determine if the resource is
+ // actually being used by the GPU right now.
+ bool isInUse = Resource->isInUse(access);
+
+ if (!isInUse) {
+ SynchronizeCsThread(SequenceNumber);
+ isInUse = Resource->isInUse(access);
+ }
+
+ if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) {
+ if (isInUse) {
// We don't have to wait, but misbehaving games may
// still try to spin on `Map` until the resource is
// idle, so we should flush pending commands
FlushImplicit(FALSE);
return false;
- } else {
+ }
+ } else {
+ if (isInUse) {
// Make sure pending commands using the resource get
// executed on the the GPU if we have to wait for it
Flush();
- SynchronizeCsThread();
-
+ SynchronizeCsThread(SequenceNumber);
+
Resource->waitIdle(access);
}
}
-
+
return true;
}
void D3D11ImmediateContext::EmitCsChunk(DxvkCsChunkRef&& chunk) {
- m_csThread.dispatchChunk(std::move(chunk));
+ m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));
m_csIsBusy = true;
}
+ void D3D11ImmediateContext::TrackTextureSequenceNumber(
+ D3D11CommonTexture* pResource,
+ UINT Subresource) {
+ pResource->TrackSequenceNumber(Subresource, m_csSeqNum + 1);
+ }
+
+
+ void D3D11ImmediateContext::TrackBufferSequenceNumber(
+ D3D11Buffer* pResource) {
+ pResource->TrackSequenceNumber(m_csSeqNum + 1);
+ }
+
+
void D3D11ImmediateContext::FlushImplicit(BOOL StrongHint) {
// Flush only if the GPU is about to go idle, in
// order to keep the number of submissions low.
diff --git a/src/d3d11/d3d11_context_imm.h b/src/d3d11/d3d11_context_imm.h
index ae57cfb13..9e9f73295 100644
--- a/src/d3d11/d3d11_context_imm.h
+++ b/src/d3d11/d3d11_context_imm.h
@@ -16,6 +16,7 @@ namespace dxvk {
class D3D11ImmediateContext : public D3D11DeviceContext {
friend class D3D11SwapChain;
friend class D3D11VideoContext;
+ friend class D3D11DeviceContext;
public:
D3D11ImmediateContext(
@@ -108,18 +109,22 @@ namespace dxvk {
const UINT* pUAVInitialCounts);
void STDMETHODCALLTYPE SwapDeviceContextState(
- ID3DDeviceContextState* pState,
- ID3DDeviceContextState** ppPreviousState);
+ ID3DDeviceContextState* pState,
+ ID3DDeviceContextState** ppPreviousState);
- void SynchronizeCsThread();
+ void SynchronizeCsThread(
+ uint64_t SequenceNumber);
private:
- DxvkCsThread m_csThread;
- bool m_csIsBusy = false;
+ DxvkCsThread m_csThread;
+ uint64_t m_csSeqNum = 0ull;
+ bool m_csIsBusy = false;
Rc m_eventSignal;
- uint64_t m_eventCount = 0;
+ uint64_t m_eventCount = 0ull;
+ uint32_t m_mappedImageCount = 0u;
+
dxvk::high_resolution_clock::time_point m_lastFlush
= dxvk::high_resolution_clock::now();
@@ -144,15 +149,30 @@ namespace dxvk {
D3D11CommonTexture* pResource,
UINT Subresource);
+ void UpdateMappedBuffer(
+ D3D11Buffer* pDstBuffer,
+ UINT Offset,
+ UINT Length,
+ const void* pSrcData,
+ UINT CopyFlags);
+
void SynchronizeDevice();
bool WaitForResource(
const Rc& Resource,
+ uint64_t SequenceNumber,
D3D11_MAP MapType,
UINT MapFlags);
void EmitCsChunk(DxvkCsChunkRef&& chunk);
+ void TrackTextureSequenceNumber(
+ D3D11CommonTexture* pResource,
+ UINT Subresource);
+
+ void TrackBufferSequenceNumber(
+ D3D11Buffer* pResource);
+
void FlushImplicit(BOOL StrongHint);
void SignalEvent(HANDLE hEvent);
diff --git a/src/d3d11/d3d11_enums.cpp b/src/d3d11/d3d11_enums.cpp
index 0b9fc0f62..24af0276c 100644
--- a/src/d3d11/d3d11_enums.cpp
+++ b/src/d3d11/d3d11_enums.cpp
@@ -9,6 +9,8 @@ std::ostream& operator << (std::ostream& os, D3D_FEATURE_LEVEL e) {
ENUM_NAME(D3D_FEATURE_LEVEL_10_1);
ENUM_NAME(D3D_FEATURE_LEVEL_11_0);
ENUM_NAME(D3D_FEATURE_LEVEL_11_1);
+ ENUM_NAME(D3D_FEATURE_LEVEL_12_0);
+ ENUM_NAME(D3D_FEATURE_LEVEL_12_1);
ENUM_DEFAULT(e);
}
}
diff --git a/src/d3d11/d3d11_interop.cpp b/src/d3d11/d3d11_interop.cpp
index 9584b6ab1..8fa50288b 100644
--- a/src/d3d11/d3d11_interop.cpp
+++ b/src/d3d11/d3d11_interop.cpp
@@ -91,7 +91,7 @@ namespace dxvk {
auto immediateContext = static_cast(deviceContext.ptr());
immediateContext->Flush();
- immediateContext->SynchronizeCsThread();
+ immediateContext->SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
}
diff --git a/src/d3d11/d3d11_main.cpp b/src/d3d11/d3d11_main.cpp
index d3f6e0c8f..d80d5dec4 100644
--- a/src/d3d11/d3d11_main.cpp
+++ b/src/d3d11/d3d11_main.cpp
@@ -242,4 +242,24 @@ extern "C" {
ppDevice, pFeatureLevel, ppImmediateContext);
}
+
+ DLLEXPORT HRESULT __stdcall D3D11On12CreateDevice(
+ IUnknown* pDevice,
+ UINT Flags,
+ const D3D_FEATURE_LEVEL* pFeatureLevels,
+ UINT FeatureLevels,
+ IUnknown* const* ppCommandQueues,
+ UINT NumQueues,
+ UINT NodeMask,
+ ID3D11Device** ppDevice,
+ ID3D11DeviceContext** ppImmediateContext,
+ D3D_FEATURE_LEVEL* pChosenFeatureLevel) {
+ static bool s_errorShown = false;
+
+ if (!std::exchange(s_errorShown, true))
+ Logger::err("D3D11On12CreateDevice: Not implemented");
+
+ return E_NOTIMPL;
+ }
+
}
\ No newline at end of file
diff --git a/src/d3d11/d3d11_resource.cpp b/src/d3d11/d3d11_resource.cpp
index 7ba71367f..5ee5fbf96 100644
--- a/src/d3d11/d3d11_resource.cpp
+++ b/src/d3d11/d3d11_resource.cpp
@@ -198,11 +198,8 @@ namespace dxvk {
}
- HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource) {
- D3D11_RESOURCE_DIMENSION dim;
- pResource->GetType(&dim);
-
- switch (dim) {
+ HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) {
+ switch (Type) {
case D3D11_RESOURCE_DIMENSION_BUFFER: static_cast (pResource)->AddRefPrivate(); return S_OK;
case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast(pResource)->AddRefPrivate(); return S_OK;
case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast(pResource)->AddRefPrivate(); return S_OK;
@@ -212,11 +209,16 @@ namespace dxvk {
}
- HRESULT ResourceReleasePrivate(ID3D11Resource* pResource) {
+ HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource) {
D3D11_RESOURCE_DIMENSION dim;
pResource->GetType(&dim);
- switch (dim) {
+ return ResourceAddRefPrivate(pResource, dim);
+ }
+
+
+ HRESULT ResourceReleasePrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) {
+ switch (Type) {
case D3D11_RESOURCE_DIMENSION_BUFFER: static_cast (pResource)->ReleasePrivate(); return S_OK;
case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast(pResource)->ReleasePrivate(); return S_OK;
case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast(pResource)->ReleasePrivate(); return S_OK;
@@ -225,4 +227,12 @@ namespace dxvk {
}
}
+
+ HRESULT ResourceReleasePrivate(ID3D11Resource* pResource) {
+ D3D11_RESOURCE_DIMENSION dim;
+ pResource->GetType(&dim);
+
+ return ResourceReleasePrivate(pResource, dim);
+ }
+
}
\ No newline at end of file
diff --git a/src/d3d11/d3d11_resource.h b/src/d3d11/d3d11_resource.h
index 7d91a3052..8f0e4b6ce 100644
--- a/src/d3d11/d3d11_resource.h
+++ b/src/d3d11/d3d11_resource.h
@@ -128,20 +128,124 @@ namespace dxvk {
* Helper method that figures out the exact type of
* the resource and calls its \c AddRefPrivate method.
* \param [in] pResource The resource to reference
+ * \param [in] Type Resource type
* \returns \c S_OK, or \c E_INVALIDARG for an invalid resource
*/
HRESULT ResourceAddRefPrivate(
- ID3D11Resource* pResource);
+ ID3D11Resource* pResource,
+ D3D11_RESOURCE_DIMENSION Type);
+ HRESULT ResourceAddRefPrivate(
+ ID3D11Resource* pResource);
+
/**
* \brief Decrements private reference count of a resource
*
* Helper method that figures out the exact type of
* the resource and calls its \c ReleasePrivate method.
* \param [in] pResource The resource to reference
+ * \param [in] Type Resource type
* \returns \c S_OK, or \c E_INVALIDARG for an invalid resource
*/
+ HRESULT ResourceReleasePrivate(
+ ID3D11Resource* pResource,
+ D3D11_RESOURCE_DIMENSION Type);
+
HRESULT ResourceReleasePrivate(
ID3D11Resource* pResource);
+ /**
+ * \brief Typed private resource pointer
+ *
+ * Stores a resource and its type, in order to avoid
+ * unnecessary GetType calls. Also optionally stores
+ * a subresource index to avoid struct padding.
+ */
+ class D3D11ResourceRef {
+
+ public:
+
+ D3D11ResourceRef()
+ : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN),
+ m_subresource(0), m_resource(nullptr) { }
+
+ D3D11ResourceRef(ID3D11Resource* pResource)
+ : D3D11ResourceRef(pResource, 0) { }
+
+ D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource)
+ : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN),
+ m_subresource(Subresource), m_resource(pResource) {
+ if (m_resource) {
+ m_resource->GetType(&m_type);
+ ResourceAddRefPrivate(m_resource, m_type);
+ }
+ }
+
+ D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource, D3D11_RESOURCE_DIMENSION Type)
+ : m_type(Type), m_subresource(Subresource), m_resource(pResource) {
+ if (m_resource)
+ ResourceAddRefPrivate(m_resource, m_type);
+ }
+
+ D3D11ResourceRef(D3D11ResourceRef&& other)
+ : m_type(other.m_type), m_resource(other.m_resource) {
+ other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN;
+ other.m_resource = nullptr;
+ }
+
+ D3D11ResourceRef(const D3D11ResourceRef& other)
+ : m_type(other.m_type), m_resource(other.m_resource) {
+ if (m_resource)
+ ResourceAddRefPrivate(m_resource, m_type);
+ }
+
+ ~D3D11ResourceRef() {
+ if (m_resource)
+ ResourceReleasePrivate(m_resource, m_type);
+ }
+
+ D3D11ResourceRef& operator = (D3D11ResourceRef&& other) {
+ if (m_resource)
+ ResourceReleasePrivate(m_resource, m_type);
+
+ m_type = other.m_type;
+ m_resource = other.m_resource;
+
+ other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN;
+ other.m_resource = nullptr;
+ return *this;
+ }
+
+ D3D11ResourceRef& operator = (const D3D11ResourceRef& other) {
+ if (other.m_resource)
+ ResourceAddRefPrivate(other.m_resource, other.m_type);
+
+ if (m_resource)
+ ResourceReleasePrivate(m_resource, m_type);
+
+ m_type = other.m_type;
+ m_resource = other.m_resource;
+ return *this;
+ }
+
+ D3D11_RESOURCE_DIMENSION GetType() const {
+ return m_type;
+ }
+
+ UINT GetSubresource() const {
+ return m_subresource;
+ }
+
+ ID3D11Resource* Get() const {
+ return m_resource;
+ }
+
+ private:
+
+ D3D11_RESOURCE_DIMENSION m_type;
+ UINT m_subresource;
+ ID3D11Resource* m_resource;
+
+ };
+
}
\ No newline at end of file
diff --git a/src/d3d11/d3d11_texture.cpp b/src/d3d11/d3d11_texture.cpp
index 69d75602d..14c1379c9 100644
--- a/src/d3d11/d3d11_texture.cpp
+++ b/src/d3d11/d3d11_texture.cpp
@@ -5,12 +5,13 @@
namespace dxvk {
D3D11CommonTexture::D3D11CommonTexture(
+ ID3D11Resource* pInterface,
D3D11Device* pDevice,
const D3D11_COMMON_TEXTURE_DESC* pDesc,
D3D11_RESOURCE_DIMENSION Dimension,
DXGI_USAGE DxgiUsage,
VkImage vkImage)
- : m_device(pDevice), m_dimension(Dimension), m_desc(*pDesc), m_dxgiUsage(DxgiUsage) {
+ : m_interface(pInterface), m_device(pDevice), m_dimension(Dimension), m_desc(*pDesc), m_dxgiUsage(DxgiUsage) {
DXGI_VK_FORMAT_MODE formatMode = GetFormatMode();
DXGI_VK_FORMAT_INFO formatInfo = m_device->LookupFormat(m_desc.Format, formatMode);
DXGI_VK_FORMAT_FAMILY formatFamily = m_device->LookupFamily(m_desc.Format, formatMode);
@@ -162,7 +163,7 @@ namespace dxvk {
if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT)
m_buffers.push_back(CreateMappedBuffer(j));
- m_mapTypes.push_back(D3D11_MAP(~0u));
+ m_mapInfo.push_back({ D3D11_MAP(~0u), 0ull });
}
}
}
@@ -910,7 +911,7 @@ namespace dxvk {
D3D11Device* pDevice,
const D3D11_COMMON_TEXTURE_DESC* pDesc)
: D3D11DeviceChild(pDevice),
- m_texture (pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE),
+ m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE),
m_interop (this, &m_texture),
m_surface (this, &m_texture),
m_resource(this),
@@ -1008,7 +1009,7 @@ namespace dxvk {
D3D11Device* pDevice,
const D3D11_COMMON_TEXTURE_DESC* pDesc)
: D3D11DeviceChild(pDevice),
- m_texture (pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE),
+ m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE),
m_interop (this, &m_texture),
m_surface (this, &m_texture),
m_resource(this),
@@ -1023,7 +1024,7 @@ namespace dxvk {
DXGI_USAGE DxgiUsage,
VkImage vkImage)
: D3D11DeviceChild(pDevice),
- m_texture (pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage),
+ m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage),
m_interop (this, &m_texture),
m_surface (this, &m_texture),
m_resource(this),
@@ -1139,7 +1140,7 @@ namespace dxvk {
D3D11Device* pDevice,
const D3D11_COMMON_TEXTURE_DESC* pDesc)
: D3D11DeviceChild(pDevice),
- m_texture (pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE),
+ m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE),
m_interop (this, &m_texture),
m_resource(this),
m_d3d10 (this) {
diff --git a/src/d3d11/d3d11_texture.h b/src/d3d11/d3d11_texture.h
index 80901a85f..e6115279d 100644
--- a/src/d3d11/d3d11_texture.h
+++ b/src/d3d11/d3d11_texture.h
@@ -1,5 +1,6 @@
#pragma once
+#include "../dxvk/dxvk_cs.h"
#include "../dxvk/dxvk_device.h"
#include "../d3d10/d3d10_texture.h"
@@ -72,6 +73,7 @@ namespace dxvk {
public:
D3D11CommonTexture(
+ ID3D11Resource* pInterface,
D3D11Device* pDevice,
const D3D11_COMMON_TEXTURE_DESC* pDesc,
D3D11_RESOURCE_DIMENSION Dimension,
@@ -80,6 +82,14 @@ namespace dxvk {
~D3D11CommonTexture();
+ /**
+ * \brief Retrieves resource interface
+ * \returns Resource interface
+ */
+ ID3D11Resource* GetInterface() const {
+ return m_interface;
+ }
+
/**
* \brief Texture properties
*
@@ -91,6 +101,14 @@ namespace dxvk {
return &m_desc;
}
+ /**
+ * \brief Retrieves D3D11 texture type
+ * \returns D3D11 resource dimension
+ */
+ D3D11_RESOURCE_DIMENSION GetDimension() const {
+ return m_dimension;
+ }
+
/**
* \brief Retrieves Vulkan image type
*
@@ -147,8 +165,8 @@ namespace dxvk {
* \returns Current map mode of that subresource
*/
D3D11_MAP GetMapType(UINT Subresource) const {
- return Subresource < m_mapTypes.size()
- ? D3D11_MAP(m_mapTypes[Subresource])
+ return Subresource < m_mapInfo.size()
+ ? D3D11_MAP(m_mapInfo[Subresource].mapType)
: D3D11_MAP(~0u);
}
@@ -159,8 +177,8 @@ namespace dxvk {
* \param [in] MapType The map type
*/
void SetMapType(UINT Subresource, D3D11_MAP MapType) {
- if (Subresource < m_mapTypes.size())
- m_mapTypes[Subresource] = MapType;
+ if (Subresource < m_mapInfo.size())
+ m_mapInfo[Subresource].mapType = MapType;
}
/**
@@ -223,6 +241,56 @@ namespace dxvk {
return m_packedFormat;
}
+ /**
+ * \brief Checks whether the resource is eligible for tracking
+ *
+ * Mapped resources with no bind flags can be tracked so that
+ * mapping them will not necessarily cause a CS thread sync.
+ * \returns \c true if tracking is supported for this resource
+ */
+ bool HasSequenceNumber() const {
+ if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_NONE)
+ return false;
+
+ // For buffer-mapped images we only need to track copies to
+ // and from that buffer, so we can safely ignore bind flags
+ if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)
+ return true;
+
+ // Otherwise we can only do accurate tracking if the
+ // image cannot be used in the rendering pipeline.
+ return m_desc.BindFlags == 0;
+ }
+
+ /**
+ * \brief Tracks sequence number for a given subresource
+ *
+ * Stores which CS chunk the resource was last used on.
+ * \param [in] Subresource Subresource index
+ * \param [in] Seq Sequence number
+ */
+ void TrackSequenceNumber(UINT Subresource, uint64_t Seq) {
+ if (Subresource < m_mapInfo.size())
+ m_mapInfo[Subresource].seq = Seq;
+ }
+
+ /**
+ * \brief Queries sequence number for a given subresource
+ *
+ * Returns which CS chunk the resource was last used on.
+ * \param [in] Subresource Subresource index
+ * \returns Sequence number for the given subresource
+ */
+ uint64_t GetSequenceNumber(UINT Subresource) {
+ if (HasSequenceNumber()) {
+ return Subresource < m_buffers.size()
+ ? m_mapInfo[Subresource].seq
+ : 0ull;
+ } else {
+ return DxvkCsThread::SynchronizeAll;
+ }
+ }
+
/**
* \brief Computes pixel offset into mapped buffer
*
@@ -310,7 +378,13 @@ namespace dxvk {
DxvkBufferSliceHandle slice;
};
- D3D11Device* const m_device;
+ struct MappedInfo {
+ D3D11_MAP mapType;
+ uint64_t seq;
+ };
+
+ ID3D11Resource* m_interface;
+ D3D11Device* m_device;
D3D11_RESOURCE_DIMENSION m_dimension;
D3D11_COMMON_TEXTURE_DESC m_desc;
D3D11_COMMON_TEXTURE_MAP_MODE m_mapMode;
@@ -319,7 +393,7 @@ namespace dxvk {
Rc m_image;
std::vector m_buffers;
- std::vector m_mapTypes;
+ std::vector m_mapInfo;
MappedBuffer CreateMappedBuffer(
UINT MipLevel) const;
diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp
index ca49ae763..d18db4056 100644
--- a/src/d3d9/d3d9_common_texture.cpp
+++ b/src/d3d9/d3d9_common_texture.cpp
@@ -40,8 +40,9 @@ namespace dxvk {
m_mapping = pDevice->LookupFormat(m_desc.Format);
- m_mapMode = DetermineMapMode();
- m_shadow = DetermineShadowState();
+ m_mapMode = DetermineMapMode();
+ m_shadow = DetermineShadowState();
+ m_supportsFetch4 = DetermineFetch4Compatibility();
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) {
bool plainSurface = m_type == D3DRTYPE_SURFACE &&
@@ -350,7 +351,7 @@ namespace dxvk {
BOOL D3D9CommonTexture::DetermineShadowState() const {
- static std::array blacklist = {
+ constexpr std::array blacklist = {
D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24
};
@@ -359,6 +360,17 @@ namespace dxvk {
}
+ BOOL D3D9CommonTexture::DetermineFetch4Compatibility() const {
+ constexpr std::array singleChannelFormats = {
+ D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24,
+ D3D9Format::R16F, D3D9Format::R32F, D3D9Format::A8,
+ D3D9Format::L8, D3D9Format::L16
+ };
+
+ return std::find(singleChannelFormats.begin(), singleChannelFormats.end(), m_desc.Format) != singleChannelFormats.end();
+ }
+
+
BOOL D3D9CommonTexture::CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const {
diff --git a/src/d3d9/d3d9_common_texture.h b/src/d3d9/d3d9_common_texture.h
index 5eefbdfd7..b3150e60d 100644
--- a/src/d3d9/d3d9_common_texture.h
+++ b/src/d3d9/d3d9_common_texture.h
@@ -191,6 +191,14 @@ namespace dxvk {
return m_shadow;
}
+ /**
+ * \brief FETCH4 compatibility
+ * \returns Whether the format of the texture supports the FETCH4 hack
+ */
+ bool SupportsFetch4() const {
+ return m_supportsFetch4;
+ }
+
/**
* \brief Null
* \returns Whether the texture is D3DFMT_NULL or not
@@ -448,6 +456,7 @@ namespace dxvk {
D3D9_VK_FORMAT_MAPPING m_mapping;
bool m_shadow; //< Depth Compare-ness
+ bool m_supportsFetch4;
int64_t m_size = 0;
@@ -487,6 +496,8 @@ namespace dxvk {
BOOL DetermineShadowState() const;
+ BOOL DetermineFetch4Compatibility() const;
+
BOOL CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const;
diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp
index 6a0839afc..23cb205c4 100644
--- a/src/d3d9/d3d9_device.cpp
+++ b/src/d3d9/d3d9_device.cpp
@@ -81,7 +81,7 @@ namespace dxvk {
, m_d3d9Options ( dxvkDevice, pParent->GetInstance()->config() )
, m_multithread ( BehaviorFlags & D3DCREATE_MULTITHREADED )
, m_isSWVP ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) ? true : false )
- , m_csThread ( dxvkDevice->createRtxContext() )
+ , m_csThread ( dxvkDevice, dxvkDevice->createRtxContext() )
, m_csChunk ( AllocCsChunk() )
// NV-DXVK start: unbound light indices
, m_state ( Direct3DState9 { D3D9CapturableState{ static_cast(std::max(m_d3d9Options.maxEnabledLights, 0)) } } )
@@ -2770,6 +2770,9 @@ namespace dxvk {
return D3D_OK;
}
+ if (!VertexCount)
+ return D3D_OK;
+
D3D9CommonBuffer* dst = static_cast(pDestBuffer)->GetCommonBuffer();
D3D9VertexDecl* decl = static_cast (pVertexDecl);
@@ -3859,10 +3862,14 @@ namespace dxvk {
constexpr DWORD Fetch4Disabled = MAKEFOURCC('G', 'E', 'T', '1');
if (unlikely(Type == D3DSAMP_MIPMAPLODBIAS)) {
+ auto texture = GetCommonTexture(m_state.textures[StateSampler]);
+ bool textureSupportsFetch4 = texture != nullptr && texture->SupportsFetch4();
+
if (unlikely(Value == Fetch4Enabled)) {
m_fetch4Enabled |= 1u << StateSampler;
- if (state[StateSampler][D3DSAMP_MAGFILTER] == D3DTEXF_POINT)
+ if (textureSupportsFetch4 && state[StateSampler][D3DSAMP_MAGFILTER] == D3DTEXF_POINT) {
m_fetch4 |= 1u << StateSampler;
+ }
}
else if (unlikely(Value == Fetch4Disabled)) {
m_fetch4Enabled &= ~(1u << StateSampler);
@@ -3871,7 +3878,10 @@ namespace dxvk {
}
if (unlikely(Type == D3DSAMP_MAGFILTER && (m_fetch4Enabled & (1u << StateSampler)))) {
- if (Value == D3DTEXF_POINT)
+ auto texture = GetCommonTexture(m_state.textures[StateSampler]);
+ bool textureSupportsFetch4 = texture != nullptr && texture->SupportsFetch4();
+
+ if (Value == D3DTEXF_POINT && textureSupportsFetch4)
m_fetch4 |= 1u << StateSampler;
else
m_fetch4 &= ~(1u << StateSampler);
@@ -3925,6 +3935,19 @@ namespace dxvk {
m_dirtySamplerStates |= 1u << StateSampler;
}
+
+ if (unlikely(m_fetch4Enabled & (1u << StateSampler) && !(m_fetch4 & (1u << StateSampler)))) {
+ bool textureSupportsFetch4 = newTexture->SupportsFetch4();
+ if (textureSupportsFetch4
+ && m_state.samplerStates[StateSampler][D3DSAMP_MAGFILTER] == D3DTEXF_POINT
+ && m_state.samplerStates[StateSampler][D3DSAMP_MINFILTER] == D3DTEXF_POINT) {
+ m_fetch4 |= 1u << StateSampler;
+ m_dirtySamplerStates |= 1u << StateSampler;
+ }
+ }
+ } else if (unlikely(m_fetch4 & (1u << StateSampler))) {
+ m_fetch4 &= ~(1u << StateSampler);
+ m_dirtySamplerStates |= 1u << StateSampler;
}
DWORD combinedUsage = oldUsage | newUsage;
@@ -5054,8 +5077,7 @@ namespace dxvk {
// recorded prior to this function will be run
FlushCsChunk();
- if (m_csThread.isBusy())
- m_csThread.synchronize();
+ m_csThread.synchronize(DxvkCsThread::SynchronizeAll);
}
diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h
index 61739621c..4b53b6e18 100644
--- a/src/d3d9/d3d9_device.h
+++ b/src/d3d9/d3d9_device.h
@@ -936,6 +936,9 @@ namespace dxvk {
// NV-DXVK start: external API
D3D9SwapchainExternal* GetExternalPresenter();
+ D3D9Rtx& RTX() {
+ return m_rtx;
+ }
// NV-DXVK end
private:
@@ -944,6 +947,9 @@ namespace dxvk {
return DxvkCsChunkRef(chunk, &m_csChunkPool);
}
+// NV-DXVK start: external API
+ public:
+// NV-DXVK end
template
void EmitCs(Cmd&& command) {
if (unlikely(!m_csChunk->push(command))) {
@@ -953,6 +959,9 @@ namespace dxvk {
m_csChunk->push(command);
}
}
+// NV-DXVK start: external API
+ private:
+// NV-DXVK end
void EmitCsChunk(DxvkCsChunkRef&& chunk);
diff --git a/src/d3d9/d3d9_main.cpp b/src/d3d9/d3d9_main.cpp
index 129910f10..8c1a5e21c 100644
--- a/src/d3d9/d3d9_main.cpp
+++ b/src/d3d9/d3d9_main.cpp
@@ -91,3 +91,12 @@ extern "C" {
return 0;
}
}
+
+// NV-DXVK start: external API
+#include
+
+void dummy() {
+ // need to reference a function so it's exported from d3d9.dll
+ remixapi_InitializeLibrary(nullptr, nullptr);
+}
+// NV-DXVK end
diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp
index 9aceef225..7110ffb18 100644
--- a/src/d3d9/d3d9_options.cpp
+++ b/src/d3d9/d3d9_options.cpp
@@ -107,13 +107,20 @@ namespace dxvk {
0, 0);
applyTristate(this->generalHazards, config.getOption("d3d9.generalHazards", Tristate::Auto));
- std::string floatEmulation = Config::toLower(config.getOption("d3d9.floatEmulation", "true"));
+ std::string floatEmulation = Config::toLower(config.getOption("d3d9.floatEmulation", "auto"));
if (floatEmulation == "strict") {
- d3d9FloatEmulation = D3D9FloatEmulation::Strict;
+ d3d9FloatEmulation = D3D9FloatEmulation::Strict;
} else if (floatEmulation == "false") {
- d3d9FloatEmulation = D3D9FloatEmulation::Disabled;
+ d3d9FloatEmulation = D3D9FloatEmulation::Disabled;
+ } else if (floatEmulation == "true") {
+ d3d9FloatEmulation = D3D9FloatEmulation::Enabled;
} else {
- d3d9FloatEmulation = D3D9FloatEmulation::Enabled;
+ bool hasMulz = adapter != nullptr
+ && adapter->matchesDriver(DxvkGpuVendor::Amd,
+ VK_DRIVER_ID_MESA_RADV,
+ VK_MAKE_VERSION(21, 99, 99),
+ 0);
+ d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled;
}
}
diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build
index 78ea3a645..69c7ceed8 100644
--- a/src/d3d9/meson.build
+++ b/src/d3d9/meson.build
@@ -94,7 +94,7 @@ d3d9_dll = shared_library('d3d9', d3d9_src, dxvk_version, glsl_generator.process
name_prefix : '',
link_with : [ util_lib ],
dependencies : [ dxso_dep, dxvk_dep, util_dep ],
- include_directories : [ dxvk_include_path, dxvk_shader_include_path, usd_include_paths ],
+ include_directories : [ dxvk_include_path, dxvk_shader_include_path, usd_include_paths, remix_api_include_path ],
install : true,
objects : not dxvk_is_msvc ? 'd3d9' + def_spec_ext : [],
vs_module_defs : 'd3d9'+def_spec_ext,
diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp
index c737f2b43..8246d20ce 100644
--- a/src/dxvk/dxvk_adapter.cpp
+++ b/src/dxvk/dxvk_adapter.cpp
@@ -938,13 +938,23 @@ namespace dxvk {
// Query full device properties for all enabled extensions
m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core);
-
- // Nvidia reports the driver version in a slightly different format
- if (DxvkGpuVendor(m_deviceInfo.core.properties.vendorID) == DxvkGpuVendor::Nvidia) {
- m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
- VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion),
- VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion >> 0) >> 2,
- VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion >> 2) >> 4);
+
+ // Some drivers reports the driver version in a slightly different format
+ switch (m_deviceInfo.khrDeviceDriverProperties.driverID) {
+ case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+ m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
+ (m_deviceInfo.core.properties.driverVersion >> 22) & 0x3ff,
+ (m_deviceInfo.core.properties.driverVersion >> 14) & 0x0ff,
+ (m_deviceInfo.core.properties.driverVersion >> 6) & 0x0ff);
+ break;
+
+ case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+ m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
+ m_deviceInfo.core.properties.driverVersion >> 14,
+ m_deviceInfo.core.properties.driverVersion & 0x3fff, 0);
+ break;
+
+ default:;
}
}
diff --git a/src/dxvk/dxvk_barrier.h b/src/dxvk/dxvk_barrier.h
index f3629fa92..f7a7713d9 100644
--- a/src/dxvk/dxvk_barrier.h
+++ b/src/dxvk/dxvk_barrier.h
@@ -354,7 +354,7 @@ namespace dxvk {
std::vector m_hashMap;
static size_t computeHash(K key) {
- return size_t(reinterpret_cast(key));
+ return size_t(uint64_t(key));
}
size_t computeIndex(K key) const {
diff --git a/src/dxvk/dxvk_buffer.cpp b/src/dxvk/dxvk_buffer.cpp
index 790081c0b..c018e4788 100644
--- a/src/dxvk/dxvk_buffer.cpp
+++ b/src/dxvk/dxvk_buffer.cpp
@@ -46,7 +46,7 @@ namespace dxvk {
m_physSliceCount = std::max(1, 256 / m_physSliceStride);
// Limit size of multi-slice buffers to reduce fragmentation
- constexpr VkDeviceSize MaxBufferSize = 4 << 20;
+ constexpr VkDeviceSize MaxBufferSize = 256 << 10;
m_physSliceMaxCount = MaxBufferSize >= m_physSliceStride
? MaxBufferSize / m_physSliceStride
@@ -128,12 +128,6 @@ namespace dxvk {
"\n usage: ", info.usage));
}
- VkMemoryAllocateFlags memoryAllocateFlags = 0;
-
- if (info.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) {
- memoryAllocateFlags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
- }
-
VkMemoryDedicatedRequirements dedicatedRequirements;
dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
dedicatedRequirements.pNext = VK_NULL_HANDLE;
@@ -177,11 +171,15 @@ namespace dxvk {
bool isGpuWritable = (m_info.access & (
VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT)) != 0;
- float priority = isGpuWritable ? 1.0f : 0.5f;
+ DxvkMemoryFlags hints(DxvkMemoryFlag::GpuReadable);
+
+ if (isGpuWritable)
+ hints.set(DxvkMemoryFlag::GpuWritable);
+
// Ask driver whether we should be using a dedicated allocation
handle.memory = m_memAlloc->alloc(&memReq.memoryRequirements,
- dedicatedRequirements, dedMemoryAllocInfo, m_memFlags, memoryAllocateFlags, priority, category);
+ dedicatedRequirements, dedMemoryAllocInfo, m_memFlags, hints, category);
if (vkd->vkBindBufferMemory(vkd->device(), handle.buffer,
handle.memory.memory(), handle.memory.offset()) != VK_SUCCESS)
diff --git a/src/dxvk/dxvk_buffer.h b/src/dxvk/dxvk_buffer.h
index 0be450ff9..44937c476 100644
--- a/src/dxvk/dxvk_buffer.h
+++ b/src/dxvk/dxvk_buffer.h
@@ -348,19 +348,22 @@ namespace dxvk {
VkDeviceAddress m_deviceAddress = 0;
uint32_t m_vertexStride = 0;
+
+ alignas(CACHE_LINE_SIZE)
+ sync::Spinlock m_freeMutex;
+
uint32_t m_lazyAlloc = false;
+ VkDeviceSize m_physSliceLength = 0;
+ VkDeviceSize m_physSliceStride = 0;
+ VkDeviceSize m_physSliceCount = 1;
+ VkDeviceSize m_physSliceMaxCount = 1;
- sync::Spinlock m_freeMutex;
- sync::Spinlock m_swapMutex;
-
- std::vector m_buffers;
- std::vector m_freeSlices;
- std::vector m_nextSlices;
-
- VkDeviceSize m_physSliceLength = 0;
- VkDeviceSize m_physSliceStride = 0;
- VkDeviceSize m_physSliceCount = 1;
- VkDeviceSize m_physSliceMaxCount = 1;
+ std::vector m_buffers;
+ std::vector m_freeSlices;
+
+ alignas(CACHE_LINE_SIZE)
+ sync::Spinlock m_swapMutex;
+ std::vector m_nextSlices;
DxvkMemoryStats::Category m_category;
diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h
index 9546f8745..13ae5e770 100644
--- a/src/dxvk/dxvk_cmdlist.h
+++ b/src/dxvk/dxvk_cmdlist.h
@@ -144,7 +144,7 @@ namespace dxvk {
* \param [in] ctr The counter to increment
* \param [in] val The value to add
*/
- void addStatCtr(DxvkStatCounter ctr, uint32_t val) {
+ void addStatCtr(DxvkStatCounter ctr, uint64_t val) {
m_statCounters.addCtr(ctr, val);
}
diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp
index f527f5cd1..f4af12175 100644
--- a/src/dxvk/dxvk_context.cpp
+++ b/src/dxvk/dxvk_context.cpp
@@ -2270,13 +2270,8 @@ namespace dxvk {
const void* data,
bool forceNoReplace) {
bool isHostVisible = buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
-
- bool replaceBuffer = !forceNoReplace
- && (size == buffer->info().size)
- && (size <= (1 << 20))
- && !isHostVisible;
-// NV-DXVK end:
-
+ bool replaceBuffer = !forceNoReplace && size == buffer->info().size && !isHostVisible;
+
DxvkBufferSliceHandle bufferSlice;
DxvkCmdBuffer cmdBuffer;
@@ -2303,20 +2298,42 @@ namespace dxvk {
m_execBarriers.recordCommands(m_cmd);
}
- // Vulkan specifies that small amounts of data (up to 64kB) can
- // be copied to a buffer directly if the size is a multiple of
- // four. Anything else must be copied through a staging buffer.
- // We'll limit the size to 4kB in order to keep command buffers
- // reasonably small, we do not know how much data apps may upload.
- if ((size <= 4096) && ((size & 0x3) == 0) && ((offset & 0x3) == 0)) {
- m_cmd->cmdUpdateBuffer(
- cmdBuffer,
- bufferSlice.handle,
- bufferSlice.offset,
- bufferSlice.length,
- data);
- }
- else {
+ m_cmd->cmdUpdateBuffer(cmdBuffer,
+ bufferSlice.handle,
+ bufferSlice.offset,
+ bufferSlice.length,
+ data);
+
+ auto& barriers = replaceBuffer
+ ? m_initBarriers
+ : m_execBarriers;
+
+ barriers.accessBuffer(
+ bufferSlice,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ buffer->info().stages,
+ buffer->info().access);
+
+ m_cmd->trackResource(buffer);
+ }
+
+// NV-DXVK begin: utility function for partial buffer uploads
+ void DxvkContext::writeToBuffer(
+ const Rc& buffer,
+ VkDeviceSize offset,
+ VkDeviceSize size,
+ const void* data,
+ bool forceNoReplace) {
+
+ if (size < 65536 && size % 4 == 0) {
+ updateBuffer(buffer, offset, size, data, forceNoReplace);
+ } else {
+ this->spillRenderPass(true);
+
+ DxvkBufferSliceHandle bufferSlice = buffer->getSliceHandle(offset, size);
+ DxvkCmdBuffer cmdBuffer = DxvkCmdBuffer::ExecBuffer;
+
auto stagingSlice = m_staging.alloc(CACHE_LINE_SIZE, size);
auto stagingHandle = stagingSlice.getSliceHandle();
@@ -2328,25 +2345,25 @@ namespace dxvk {
region.size = size;
m_cmd->cmdCopyBuffer(cmdBuffer,
- stagingHandle.handle, bufferSlice.handle, 1, ®ion);
+ stagingHandle.handle,
+ bufferSlice.handle,
+ 1,
+ ®ion);
m_cmd->trackResource(stagingSlice.buffer());
- }
- auto& barriers = replaceBuffer
- ? m_initBarriers
- : m_execBarriers;
-
- barriers.accessBuffer(
- bufferSlice,
- VK_PIPELINE_STAGE_TRANSFER_BIT,
- VK_ACCESS_TRANSFER_WRITE_BIT,
- buffer->info().stages,
- buffer->info().access);
+ auto& barriers = m_execBarriers;
+ barriers.accessBuffer(
+ bufferSlice,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ buffer->info().stages,
+ buffer->info().access);
- m_cmd->trackResource(buffer);
+ m_cmd->trackResource(buffer);
+ }
}
-
+// NV-DXVK end
void DxvkContext::updateImage(
const Rc& image,
diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h
index 9092aef78..ad7a302dd 100644
--- a/src/dxvk/dxvk_context.h
+++ b/src/dxvk/dxvk_context.h
@@ -857,6 +857,15 @@ namespace dxvk {
bool forceNoReplace = false);
// NV-DXVK end:
+ // NV-DXVK begin: utility function for partial buffer uploads
+ void writeToBuffer(
+ const Rc& buffer,
+ VkDeviceSize offset,
+ VkDeviceSize size,
+ const void* data,
+ bool forceNoReplace = false);
+ // NV-DXVK end
+
/**
* \brief Updates an image
*
@@ -1213,6 +1222,19 @@ namespace dxvk {
*/
void insertDebugLabel(VkDebugUtilsLabelEXT *label);
+ /**
+ * \brief Increments a given stat counter
+ *
+ * The stat counters will be merged into the global
+ * stat counters upon execution of the command list.
+ * \param [in] counter Stat counter to increment
+ * \param [in] value Increment value
+ */
+ void addStatCtr(DxvkStatCounter counter, uint64_t value) {
+ if (m_cmd != nullptr)
+ m_cmd->addStatCtr(counter, value);
+ }
+
// NV-DXVK start: use EXT_debug_utils
VkDescriptorSet allocateDescriptorSet(VkDescriptorSetLayout layout, const char *name = nullptr);
// NV-DXVK end
@@ -1229,6 +1251,7 @@ namespace dxvk {
}
protected:
+
Rc m_device;
DxvkObjects* m_common;
diff --git a/src/dxvk/dxvk_cs.cpp b/src/dxvk/dxvk_cs.cpp
index d28b5d650..5b91acf2c 100644
--- a/src/dxvk/dxvk_cs.cpp
+++ b/src/dxvk/dxvk_cs.cpp
@@ -97,8 +97,11 @@ namespace dxvk {
}
- DxvkCsThread::DxvkCsThread(const Rc& context)
- : m_context(context), m_thread([this] { threadFunc(); }) {
+ DxvkCsThread::DxvkCsThread(
+ const Rc& device,
+ const Rc& context)
+ : m_device(device), m_context(context),
+ m_thread([this] { threadFunc(); }) {
}
@@ -113,26 +116,42 @@ namespace dxvk {
}
- void DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {
+ uint64_t DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {
ScopedCpuProfileZone();
+ uint64_t seq;
+
{ std::unique_lock lock(m_mutex);
+ seq = ++m_chunksDispatched;
m_chunksQueued.push(std::move(chunk));
- m_chunksPending += 1;
}
m_condOnAdd.notify_one();
+ return seq;
}
- void DxvkCsThread::synchronize() {
+ void DxvkCsThread::synchronize(uint64_t seq) {
ScopedCpuProfileZone();
- std::unique_lock lock(m_mutex);
-
- m_condOnSync.wait(lock, [this] {
- return !m_chunksPending.load();
- });
+ // Avoid locking if we know the sync is a no-op, may
+ // reduce overhead if this is being called frequently
+ if (seq > m_chunksExecuted.load(std::memory_order_acquire)) {
+ std::unique_lock lock(m_mutex);
+
+ if (seq == SynchronizeAll)
+ seq = m_chunksDispatched.load();
+
+ auto t0 = dxvk::high_resolution_clock::now();
+ m_condOnSync.wait(lock, [this, seq] {
+ return m_chunksExecuted.load() >= seq;
+ });
+ auto t1 = dxvk::high_resolution_clock::now();
+ auto ticks = std::chrono::duration_cast(t1 - t0);
+
+ m_device->addStatCtr(DxvkStatCounter::CsSyncCount, 1);
+ m_device->addStatCtr(DxvkStatCounter::CsSyncTicks, ticks.count());
+ }
}
@@ -147,8 +166,8 @@ namespace dxvk {
while (!m_stopped.load()) {
{ std::unique_lock lock(m_mutex);
if (chunk) {
- if (--m_chunksPending == 0)
- m_condOnSync.notify_one();
+ m_chunksExecuted++;
+ m_condOnSync.notify_one();
chunk = DxvkCsChunkRef();
}
@@ -166,8 +185,10 @@ namespace dxvk {
}
}
- if (chunk)
+ if (chunk) {
+ m_context->addStatCtr(DxvkStatCounter::CsChunkCount, 1);
chunk->executeAll(m_context.ptr());
+ }
}
} catch (const DxvkError& e) {
Logger::err("Exception on CS thread!");
diff --git a/src/dxvk/dxvk_cs.h b/src/dxvk/dxvk_cs.h
index ca34fa23f..94f980dfd 100644
--- a/src/dxvk/dxvk_cs.h
+++ b/src/dxvk/dxvk_cs.h
@@ -6,6 +6,8 @@
#include
#include "../util/thread.h"
+
+#include "dxvk_device.h"
#include "dxvk_context.h"
namespace dxvk {
@@ -45,7 +47,7 @@ namespace dxvk {
* \brief Executes embedded commands
* \param [in] ctx The target context
*/
- virtual void exec(DxvkContext* ctx) const = 0;
+ virtual void exec(DxvkContext* ctx) = 0;
private:
@@ -71,7 +73,7 @@ namespace dxvk {
DxvkCsTypedCmd (DxvkCsTypedCmd&&) = delete;
DxvkCsTypedCmd& operator = (DxvkCsTypedCmd&&) = delete;
- void exec(DxvkContext* ctx) const {
+ void exec(DxvkContext* ctx) override {
m_command(ctx);
}
@@ -102,7 +104,7 @@ namespace dxvk {
DxvkCsDataCmd (DxvkCsDataCmd&&) = delete;
DxvkCsDataCmd& operator = (DxvkCsDataCmd&&) = delete;
- void exec(DxvkContext* ctx) const {
+ void exec(DxvkContext* ctx) override {
m_command(ctx, &m_data);
}
@@ -379,8 +381,12 @@ namespace dxvk {
class DxvkCsThread {
public:
-
- DxvkCsThread(const Rc& context);
+
+ constexpr static uint64_t SynchronizeAll = ~0ull;
+
+ DxvkCsThread(
+ const Rc& device,
+ const Rc& context);
~DxvkCsThread();
/**
@@ -389,41 +395,44 @@ namespace dxvk {
* Can be used to efficiently play back large
* command lists recorded on another thread.
* \param [in] chunk The chunk to dispatch
+ * \returns Sequence number of the submission
*/
- void dispatchChunk(DxvkCsChunkRef&& chunk);
+ uint64_t dispatchChunk(DxvkCsChunkRef&& chunk);
/**
* \brief Synchronizes with the thread
*
- * This waits for all chunks in the dispatch
- * queue to be processed by the thread. Note
- * that this does \e not implicitly call
- * \ref flush.
+ * This waits for all chunks in the dispatch queue to
+ * be processed by the thread, up to the given sequence
+ * number. If the sequence number is 0, this will wait
+ * for all pending chunks to complete execution.
+ * \param [in] seq Sequence number to wait for.
*/
- void synchronize();
+ void synchronize(uint64_t seq);
/**
- * \brief Checks whether the worker thread is busy
- *
- * Note that this information is only reliable if
- * only the calling thread dispatches jobs to the
- * worker queue and if the result is \c false.
- * \returns \c true if there is still work to do
+ * \brief Retrieves last executed sequence number
+ *
+ * Can be used to avoid synchronization in some cases.
+ * \returns Sequence number of last executed chunk
*/
- bool isBusy() const {
- return m_chunksPending.load() != 0;
+ uint64_t lastSequenceNumber() const {
+ return m_chunksExecuted.load();
}
-
+
private:
- const Rc m_context;
+ Rc m_device;
+ Rc m_context;
+
+ std::atomic m_chunksDispatched = { 0ull };
+ std::atomic m_chunksExecuted = { 0ull };
std::atomic m_stopped = { false };
dxvk::mutex m_mutex;
dxvk::condition_variable m_condOnAdd;
dxvk::condition_variable m_condOnSync;
std::queue m_chunksQueued;
- std::atomic m_chunksPending = { 0u };
dxvk::thread m_thread;
void threadFunc();
diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h
index 579219c90..f6ade334e 100644
--- a/src/dxvk/dxvk_device.h
+++ b/src/dxvk/dxvk_device.h
@@ -551,6 +551,17 @@ namespace dxvk {
return m_submissionQueue.pendingSubmissions();
}
+ /**
+ * \brief Increments a given stat counter
+ *
+ * \param [in] counter Stat counter to increment
+ * \param [in] value Increment value
+ */
+ void addStatCtr(DxvkStatCounter counter, uint64_t value) {
+ std::lock_guard lock(m_statLock);
+ m_statCounters.addCtr(counter, value);
+ }
+
/**
* \brief Waits for a given submission
*
diff --git a/src/dxvk/dxvk_framebuffer.h b/src/dxvk/dxvk_framebuffer.h
index eaeb62c10..b55e6e5fa 100644
--- a/src/dxvk/dxvk_framebuffer.h
+++ b/src/dxvk/dxvk_framebuffer.h
@@ -65,7 +65,7 @@ namespace dxvk {
state.add(depthView);
for (uint32_t i = 0; i < MaxNumRenderTargets; i++)
state.add(colorViews[i]);
- state.add(reinterpret_cast(renderPass));
+ state.add(uint64_t(renderPass));
return state;
}
diff --git a/src/dxvk/dxvk_image.cpp b/src/dxvk/dxvk_image.cpp
index ff13e1956..06fd3b99a 100644
--- a/src/dxvk/dxvk_image.cpp
+++ b/src/dxvk/dxvk_image.cpp
@@ -137,7 +137,7 @@ namespace dxvk {
m_vkd->vkGetImageMemoryRequirements2(
m_vkd->device(), &memReqInfo, &memReq);
-
+
if (info.tiling != VK_IMAGE_TILING_LINEAR && !dedicatedRequirements.prefersDedicatedAllocation) {
memReq.memoryRequirements.size = align(memReq.memoryRequirements.size, memAlloc.bufferImageGranularity());
memReq.memoryRequirements.alignment = align(memReq.memoryRequirements.alignment , memAlloc.bufferImageGranularity());
@@ -151,8 +151,11 @@ namespace dxvk {
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)) != 0;
- float priority = isGpuWritable ? 1.0f : 0.5f;
+ DxvkMemoryFlags hints(DxvkMemoryFlag::GpuReadable);
+ if (isGpuWritable)
+ hints.set(DxvkMemoryFlag::GpuWritable);
+
if (m_shared) {
dedicatedRequirements.prefersDedicatedAllocation = VK_TRUE;
dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE;
@@ -160,7 +163,7 @@ namespace dxvk {
// Ask driver whether we should be using a dedicated allocation
m_image.memory = memAlloc.alloc(&memReq.memoryRequirements,
- dedicatedRequirements, dedMemoryAllocInfo, memFlags, 0, priority, category);
+ dedicatedRequirements, dedMemoryAllocInfo, memFlags, hints, category);
// Try to bind the allocated memory slice to the image
if (m_vkd->vkBindImageMemory(m_vkd->device(), m_image.image,
diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp
index e4fb014a9..cb46d5001 100644
--- a/src/dxvk/dxvk_instance.cpp
+++ b/src/dxvk/dxvk_instance.cpp
@@ -236,12 +236,8 @@ namespace dxvk {
// NV-DXVK end
m_extProviders.push_back(&DxvkPlatformExts::s_instance);
-
- if (m_options.enableOpenVR)
- m_extProviders.push_back(&VrInstance::s_instance);
-
- if (m_options.enableOpenXR)
- m_extProviders.push_back(&DxvkXrProvider::s_instance);
+ m_extProviders.push_back(&VrInstance::s_instance);
+ m_extProviders.push_back(&DxvkXrProvider::s_instance);
// NV-DXVK start: RTXIO
#ifdef WITH_RTXIO
@@ -408,7 +404,7 @@ namespace dxvk {
// NV-DXVK start: custom pEngineName
appInfo.pEngineName = "DXVK_NvRemix";
// NV-DXVK end
- appInfo.engineVersion = VK_MAKE_VERSION(1, 9, 3);
+ appInfo.engineVersion = VK_MAKE_VERSION(1, 9, 4);
// NV-DXVK start: Require Vulkan 1.3
appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0);
// NV-DXVK end
diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp
index fd4a2d010..adf1cb31c 100644
--- a/src/dxvk/dxvk_memory.cpp
+++ b/src/dxvk/dxvk_memory.cpp
@@ -19,6 +19,9 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+
+#include
+
#include "dxvk_device.h"
#include "dxvk_memory.h"
@@ -221,8 +224,9 @@ DxvkMemory::DxvkMemory() { }
DxvkMemoryChunk::DxvkMemoryChunk(
DxvkMemoryAllocator* alloc,
DxvkMemoryType* type,
- DxvkDeviceMemory memory)
- : m_alloc(alloc), m_type(type), m_memory(memory) {
+ DxvkDeviceMemory memory,
+ DxvkMemoryFlags hints)
+ : m_alloc(alloc), m_type(type), m_memory(memory), m_hints(hints) {
// Mark the entire chunk as free
m_freeList.push_back(FreeSlice { 0, memory.memSize });
}
@@ -236,17 +240,14 @@ DxvkMemory::DxvkMemory() { }
DxvkMemory DxvkMemoryChunk::alloc(
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
+ VkMemoryPropertyFlags flags,
VkDeviceSize size,
VkDeviceSize align,
- float priority,
+ DxvkMemoryFlags hints,
DxvkMemoryStats::Category category) {
- // Property and allocate flags must be compatible. This could
+ // Property flags must be compatible. This could
// be refined a bit in the future if necessary.
- if (m_memory.memPropertyFlags != propertyFlags
- || m_memory.memAllocateFlags != allocateFlags
- || m_memory.priority != priority)
+ if (m_memory.memFlags != flags || !checkHints(hints))
return DxvkMemory();
// If the chunk is full, return
@@ -322,20 +323,30 @@ DxvkMemory::DxvkMemory() { }
m_freeList.push_back({ offset, length });
}
- // NV-DXVK start: Free unused memory
- bool DxvkMemoryChunk::isWholeChunkFree() const {
- if (m_freeList.size() != 1)
- return false;
+
+ bool DxvkMemoryChunk::isEmpty() const {
+ return m_freeList.size() == 1
+ && m_freeList[0].length == m_memory.memSize;
+ }
+
+
+ bool DxvkMemoryChunk::isCompatible(const Rc& other) const {
+ return other->m_memory.memFlags == m_memory.memFlags && other->m_hints == m_hints;
+ }
+
- if (m_freeList[0].offset != 0)
- return false;
+ bool DxvkMemoryChunk::checkHints(DxvkMemoryFlags hints) const {
+ DxvkMemoryFlags mask(
+ DxvkMemoryFlag::Small,
+ DxvkMemoryFlag::GpuReadable,
+ DxvkMemoryFlag::GpuWritable);
- if (m_freeList[0].length != m_memory.memSize)
- return false;
+ if (hints.test(DxvkMemoryFlag::IgnoreConstraints))
+ mask = DxvkMemoryFlags();
- return true;
+ return (m_hints & mask) == (hints & mask);
}
- // NV-DXVK end
+
DxvkMemoryAllocator::DxvkMemoryAllocator(const DxvkDevice* device)
: m_vkd (device->vkd()),
@@ -358,7 +369,17 @@ DxvkMemory::DxvkMemory() { }
m_memTypes[i].heapId = m_memProps.memoryTypes[i].heapIndex;
m_memTypes[i].memType = m_memProps.memoryTypes[i];
m_memTypes[i].memTypeId = i;
- m_memTypes[i].chunkSize = pickChunkSize(i);
+ }
+
+ /* Check what kind of heap the HVV memory type is on, if any. If the
+ * HVV memory type is on the largest device-local heap, we either have
+ * an UMA system or an RBAR-enabled system. Otherwise, there will likely
+ * be a separate, smaller heap for it. */
+ VkDeviceSize largestDeviceLocalHeap = 0;
+
+ for (uint32_t i = 0; i < m_memProps.memoryTypeCount; i++) {
+ if (m_memTypes[i].memType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ largestDeviceLocalHeap = std::max(largestDeviceLocalHeap, m_memTypes[i].heap->properties.size);
}
/* Work around an issue on Nvidia drivers where using the entire
@@ -371,12 +392,11 @@ DxvkMemory::DxvkMemory() { }
if (shrinkNvidiaHvvHeap) {
for (uint32_t i = 0; i < m_memProps.memoryTypeCount; i++) {
- VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ VkMemoryPropertyFlags hvvFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
- if ((m_memTypes[i].memType.propertyFlags & flags) == flags) {
+ if ((m_memTypes[i].memType.propertyFlags & hvvFlags) == hvvFlags
+ && (m_memTypes[i].heap->properties.size < largestDeviceLocalHeap))
m_memTypes[i].heap->budget = 32 << 20;
- m_memTypes[i].chunkSize = 1 << 20;
- }
}
}
}
@@ -392,9 +412,8 @@ DxvkMemory::DxvkMemory() { }
const VkMemoryRequirements* req,
const VkMemoryDedicatedRequirements& dedAllocReq,
const VkMemoryDedicatedAllocateInfo& dedAllocInfo,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- float priority,
+ VkMemoryPropertyFlags flags,
+ DxvkMemoryFlags hints,
DxvkMemoryStats::Category category) {
ScopedCpuProfileZone();
@@ -402,26 +421,45 @@ DxvkMemory::DxvkMemory() { }
// Note: The mutex here in DXVK has been removed in favor of the per-memory type mutex in tryAllocFromType.
// NV-DXVK end
+ // Keep small allocations together to avoid fragmenting
+ // chunks for larger resources with lots of small gaps,
+ // as well as resources with potentially weird lifetimes
+ if (req->size <= SmallAllocationThreshold) {
+ hints.set(DxvkMemoryFlag::Small);
+ hints.clr(DxvkMemoryFlag::GpuWritable, DxvkMemoryFlag::GpuReadable);
+ }
+
+ // Ignore all hints for host-visible allocations since they
+ // usually don't make much sense for those resources
+ if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ hints = DxvkMemoryFlags();
+
// Try to allocate from a memory type which supports the given flags exactly
auto dedAllocPtr = dedAllocReq.prefersDedicatedAllocation ? &dedAllocInfo : nullptr;
- DxvkMemory result = this->tryAlloc(req, dedAllocPtr, propertyFlags, allocateFlags, priority, category);
+ DxvkMemory result = this->tryAlloc(req, dedAllocPtr, flags, hints, category);
// If the first attempt failed, try ignoring the dedicated allocation
if (!result && dedAllocPtr && !dedAllocReq.requiresDedicatedAllocation) {
- result = this->tryAlloc(req, nullptr, propertyFlags, allocateFlags, priority, category);
+ result = this->tryAlloc(req, nullptr, flags, hints, category);
dedAllocPtr = nullptr;
}
+ // Retry without the hint constraints
+ if (!result) {
+ hints.set(DxvkMemoryFlag::IgnoreConstraints);
+ result = this->tryAlloc(req, nullptr, flags, hints, category);
+ }
+
// If that still didn't work, probe slower memory types as well
VkMemoryPropertyFlags optFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
VkMemoryPropertyFlags remFlags = 0;
- while (!result && (propertyFlags & optFlags)) {
+ while (!result && (flags & optFlags)) {
remFlags |= optFlags & (0 - optFlags); // Note: 0 - x is a more well defined version of -x for unsigned values
optFlags &= ~remFlags;
- result = this->tryAlloc(req, dedAllocPtr, propertyFlags & ~remFlags, allocateFlags, priority, category);
+ result = this->tryAlloc(req, dedAllocPtr, flags & ~remFlags, hints, category);
}
if (!result) {
@@ -431,8 +469,7 @@ DxvkMemory::DxvkMemory() { }
"DxvkMemoryAllocator: Memory allocation failed",
"\n Size: ", req->size,
"\n Alignment: ", req->alignment,
- "\n Mem property flags: ", "0x", std::hex, propertyFlags,
- "\n Mem allocate flags: ", "0x", std::hex, allocateFlags,
+ "\n Mem property flags: ", "0x", std::hex, flags,
"\n Mem types: ", "0x", std::hex, req->memoryTypeBits));
for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) {
@@ -454,36 +491,29 @@ DxvkMemory::DxvkMemory() { }
return result;
}
- // NV-DXVK start: Free unused memory
+ //// NV-DXVK start: Free unused memory
void DxvkMemoryAllocator::freeUnusedChunks() {
- for (auto& type : m_memTypes) {
- std::lock_guard lock(type.mutex);
-
- const auto new_end_iterator = std::remove_if(type.chunks.begin(), type.chunks.end(), [](const auto& chunk) {
- return chunk->isWholeChunkFree();
- });
-
- type.chunks.erase(new_end_iterator, type.chunks.end());
+ for (auto& heap : m_memHeaps) {
+ freeEmptyChunks(&heap);
}
}
- // NV-DXVK end
+ //// NV-DXVK end
DxvkMemory DxvkMemoryAllocator::tryAlloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- float priority,
+ VkMemoryPropertyFlags flags,
+ DxvkMemoryFlags hints,
DxvkMemoryStats::Category category) {
DxvkMemory result;
for (uint32_t i = 0; i < m_memProps.memoryTypeCount && !result; i++) {
const bool supported = (req->memoryTypeBits & (1u << i)) != 0;
- const bool adequate = (m_memTypes[i].memType.propertyFlags & propertyFlags) == propertyFlags;
+ const bool adequate = (m_memTypes[i].memType.propertyFlags & flags) == flags;
if (supported && adequate) {
result = this->tryAllocFromType(&m_memTypes[i],
- propertyFlags, allocateFlags, req->size, req->alignment, priority, dedAllocInfo, category);
+ flags, req->size, req->alignment, hints, dedAllocInfo, category);
}
}
@@ -493,11 +523,10 @@ DxvkMemory::DxvkMemory() { }
DxvkMemory DxvkMemoryAllocator::tryAllocFromType(
DxvkMemoryType* type,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
+ VkMemoryPropertyFlags flags,
VkDeviceSize size,
VkDeviceSize align,
- float priority,
+ DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
DxvkMemoryStats::Category category
) {
@@ -506,32 +535,47 @@ DxvkMemory::DxvkMemory() { }
// NV-DXVK end
// Prevent unnecessary external host memory fragmentation
- bool isDeviceLocal = (propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0;
-
- if (!isDeviceLocal)
- priority = 0.0f;
+ bool isDeviceLocal = (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0;
+
+ VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, hints);
DxvkMemory memory;
- if (size >= type->chunkSize || dedAllocInfo) {
+ if (size >= chunkSize || dedAllocInfo) {
+ if (this->shouldFreeEmptyChunks(type->heap, size)) {
+ // NV-DXVK start: use a per-memory-type mutex
+ type->mutex.unlock();
+ this->freeEmptyChunks(type->heap);
+ type->mutex.lock();
+ // NV-DXVK end
+ }
+
DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(
- type, propertyFlags, allocateFlags, size, priority, dedAllocInfo, category);
+ type, flags, size, hints, dedAllocInfo, category);
if (devMem.memHandle != VK_NULL_HANDLE)
memory = DxvkMemory(this, nullptr, type, devMem.memHandle, 0, size, devMem.memPointer, category);
} else {
for (uint32_t i = 0; i < type->chunks.size() && !memory; i++)
- memory = type->chunks[i]->alloc(propertyFlags, allocateFlags, size, align, priority, category);
+ memory = type->chunks[i]->alloc(flags, size, align, hints, category);
if (!memory) {
DxvkDeviceMemory devMem;
-
- for (uint32_t i = 0; i < 6 && (type->chunkSize >> i) >= size && !devMem.memHandle; i++)
- devMem = tryAllocDeviceMemory(type, propertyFlags, allocateFlags, type->chunkSize >> i, priority, nullptr, category);
+
+ if (this->shouldFreeEmptyChunks(type->heap, chunkSize)) {
+ // NV-DXVK start: use a per-memory-type mutex
+ type->mutex.unlock();
+ this->freeEmptyChunks(type->heap);
+ type->mutex.lock();
+ // NV-DXVK end
+ }
+
+ for (uint32_t i = 0; i < 6 && (chunkSize >> i) >= size && !devMem.memHandle; i++)
+ devMem = tryAllocDeviceMemory(type, flags, chunkSize >> i, hints, nullptr, category);
if (devMem.memHandle) {
- Rc chunk = new DxvkMemoryChunk(this, type, devMem);
- memory = chunk->alloc(propertyFlags, allocateFlags, size, align, priority, category);
+ Rc chunk = new DxvkMemoryChunk(this, type, devMem, hints);
+ memory = chunk->alloc(flags, size, align, hints, category);
type->chunks.push_back(std::move(chunk));
}
@@ -547,29 +591,38 @@ DxvkMemory::DxvkMemory() { }
DxvkDeviceMemory DxvkMemoryAllocator::tryAllocDeviceMemory(
DxvkMemoryType* type,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
+ VkMemoryPropertyFlags flags,
VkDeviceSize size,
- float priority,
+ DxvkMemoryFlags hints,
const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
DxvkMemoryStats::Category category) {
ScopedCpuProfileZone();
- bool useMemoryPriority = (propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ bool useMemoryPriority = (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
&& (m_device->features().extMemoryPriority.memoryPriority);
if (type->heap->budget && type->heap->stats.totalAllocated() + size > type->heap->budget)
return DxvkDeviceMemory();
+ float priority = 0.0f;
+
+ if (hints.test(DxvkMemoryFlag::GpuReadable))
+ priority = 0.5f;
+ if (hints.test(DxvkMemoryFlag::GpuWritable))
+ priority = 1.0f;
+
DxvkDeviceMemory result;
result.memSize = size;
- result.memPropertyFlags = propertyFlags;
- result.memAllocateFlags = allocateFlags;
+ result.memFlags = flags;
result.priority = priority;
VkMemoryAllocateFlagsInfo allocateFlagsInfo;
allocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
allocateFlagsInfo.pNext = dedAllocInfo;
- allocateFlagsInfo.flags = allocateFlags;
+ // NV-DXVK begin: use device address bit for allocations
+ // dxvk-remix requires buffer device addresses on some allocations; setting this bit
+ // is essentially free, so we set it unconditionally to avoid having to plumb it through
+ allocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
+ // NV-DXVK end
allocateFlagsInfo.deviceMask = 0;
VkMemoryPriorityAllocateInfoEXT prio;
@@ -586,7 +639,7 @@ DxvkMemory::DxvkMemory() { }
if (m_vkd->vkAllocateMemory(m_vkd->device(), &info, nullptr, &result.memHandle) != VK_SUCCESS)
return DxvkDeviceMemory();
- if (propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
VkResult status = m_vkd->vkMapMemory(m_vkd->device(), result.memHandle, 0, VK_WHOLE_SIZE, 0, &result.memPointer);
if (status != VK_SUCCESS) {
@@ -631,6 +684,18 @@ DxvkMemory::DxvkMemory() { }
VkDeviceSize offset,
VkDeviceSize length) {
chunk->free(offset, length);
+
+ if (chunk->isEmpty()) {
+ Rc chunkRef = chunk;
+
+ // Free the chunk if we have to, or at least put it at the end of
+ // the list so that chunks that are already in use and cannot be
+ // freed are prioritized for allocations to reduce memory pressure.
+ type->chunks.erase(std::remove(type->chunks.begin(), type->chunks.end(), chunkRef));
+
+ if (!this->shouldFreeChunk(type, chunkRef))
+ type->chunks.push_back(std::move(chunkRef));
+ }
}
@@ -643,7 +708,7 @@ DxvkMemory::DxvkMemory() { }
}
- VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId) const {
+ VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId, DxvkMemoryFlags hints) const {
VkMemoryType type = m_memProps.memoryTypes[memTypeId];
VkMemoryHeap heap = m_memProps.memoryHeaps[type.heapIndex];
@@ -653,11 +718,14 @@ DxvkMemory::DxvkMemory() { }
VkDeviceSize chunkSize = (isDeviceLocal ? options.deviceLocalMemoryChunkSizeMB : options.otherMemoryChunkSizeMB) << 20;
// NV-DXVK end
- // Try to waste a bit less system memory in 32-bit
- // applications due to address space constraints
+ if (hints.test(DxvkMemoryFlag::Small))
+ chunkSize = 16 << 20;
+
+ // Try to waste a bit less system memory especially in
+ // 32-bit applications due to address space constraints
if (env::is32BitHostPlatform()) {
if (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
- chunkSize = 32 << 20;
+ chunkSize = 16 << 20;
}
// Reduce the chunk size on small heaps so
@@ -667,5 +735,55 @@ DxvkMemory::DxvkMemory() { }
return chunkSize;
}
-
+
+
+ bool DxvkMemoryAllocator::shouldFreeChunk(
+ const DxvkMemoryType* type,
+ const Rc& chunk) const {
+ // Under memory pressure, we should start freeing everything.
+ if (this->shouldFreeEmptyChunks(type->heap, 0))
+ return true;
+
+ // Even if we have enough memory to spare, only keep
+ // one chunk of each type around to save memory.
+ for (const auto& c : type->chunks) {
+ if (c != chunk && c->isEmpty() && c->isCompatible(chunk))
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool DxvkMemoryAllocator::shouldFreeEmptyChunks(
+ const DxvkMemoryHeap* heap,
+ VkDeviceSize allocationSize) const {
+ VkDeviceSize budget = heap->budget;
+
+ if (!budget)
+ budget = (heap->properties.size * 4) / 5;
+
+ return heap->stats.totalAllocated() + allocationSize > budget;
+ }
+
+
+ void DxvkMemoryAllocator::freeEmptyChunks(
+ const DxvkMemoryHeap* heap) {
+ for (uint32_t i = 0; i < m_memProps.memoryTypeCount; i++) {
+ DxvkMemoryType* type = &m_memTypes[i];
+
+ if (type->heap != heap)
+ continue;
+
+ // NV-DXVK start: use a per-memory-type mutex
+ std::lock_guard lock(type->mutex);
+ // NV-DXVK end
+
+ type->chunks.erase(
+ std::remove_if(type->chunks.begin(), type->chunks.end(),
+ [] (const Rc& chunk) { return chunk->isEmpty(); }),
+ type->chunks.end());
+ }
+ }
+
}
diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h
index 35f555f3d..a74b5b39d 100644
--- a/src/dxvk/dxvk_memory.h
+++ b/src/dxvk/dxvk_memory.h
@@ -125,8 +125,7 @@ namespace dxvk {
VkDeviceMemory memHandle = VK_NULL_HANDLE;
void* memPointer = nullptr;
VkDeviceSize memSize = 0;
- VkMemoryPropertyFlags memPropertyFlags = 0;
- VkMemoryAllocateFlags memAllocateFlags = 0;
+ VkMemoryPropertyFlags memFlags = 0;
float priority = 0.0f;
};
@@ -158,8 +157,6 @@ namespace dxvk {
VkMemoryType memType;
uint32_t memTypeId;
- VkDeviceSize chunkSize;
-
std::vector> chunks;
// NV-DXVK start: use a per-memory-type mutex rather than an allocator-wide mutex
@@ -259,6 +256,22 @@ namespace dxvk {
void free();
};
+
+
+ /**
+ * \brief Memory allocation flags
+ *
+ * Used to batch similar allocations into the same
+ * set of chunks, which may help with fragmentation.
+ */
+ enum class DxvkMemoryFlag : uint32_t {
+ Small = 0, ///< Small allocation
+ GpuReadable = 1, ///< Medium-priority resource
+ GpuWritable = 2, ///< High-priority resource
+ IgnoreConstraints = 3, ///< Ignore most allocation flags
+ };
+
+ using DxvkMemoryFlags = Flags;
/**
@@ -274,7 +287,8 @@ namespace dxvk {
DxvkMemoryChunk(
DxvkMemoryAllocator* alloc,
DxvkMemoryType* type,
- DxvkDeviceMemory memory);
+ DxvkDeviceMemory memory,
+ DxvkMemoryFlags m_hints);
~DxvkMemoryChunk();
@@ -283,19 +297,18 @@ namespace dxvk {
*
* On failure, this returns a slice with
* \c VK_NULL_HANDLE as the memory handle.
- * \param [in] flags Requested memory flags
+ * \param [in] flags Requested memory type flags
* \param [in] size Number of bytes to allocate
* \param [in] align Required alignment
- * \param [in] priority Requested priority
+ * \param [in] hints Memory category
* \returns The allocated memory slice
*/
DxvkMemory alloc(
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- VkDeviceSize size,
- VkDeviceSize align,
- float priority,
- DxvkMemoryStats::Category category);
+ VkMemoryPropertyFlags flags,
+ VkDeviceSize size,
+ VkDeviceSize align,
+ DxvkMemoryFlags hints,
+ DxvkMemoryStats::Category category);
/**
* \brief Frees memory
@@ -309,16 +322,18 @@ namespace dxvk {
void free(
VkDeviceSize offset,
VkDeviceSize length);
-
- // NV-DXVK start: Free unused memory
+
/**
- * \brief Queries if an entire chunk is considered free.
- *
- * Returns true if no allocations exist
- * on this chunk.
+ * \brief Checks whether the chunk is being used
+ * \returns \c true if there are no allocations left
*/
- bool isWholeChunkFree() const;
- // NV-DXVK end
+ bool isEmpty() const;
+
+ /**
+ * \brief Checks whether hints and flags of another chunk match
+ * \param [in] other The chunk to compare to
+ */
+ bool isCompatible(const Rc& other) const;
private:
@@ -330,8 +345,11 @@ namespace dxvk {
DxvkMemoryAllocator* m_alloc;
DxvkMemoryType* m_type;
DxvkDeviceMemory m_memory;
+ DxvkMemoryFlags m_hints;
std::vector m_freeList;
+
+ bool checkHints(DxvkMemoryFlags hints) const;
};
@@ -345,6 +363,8 @@ namespace dxvk {
class DxvkMemoryAllocator {
friend class DxvkMemory;
friend class DxvkMemoryChunk;
+
+ constexpr static VkDeviceSize SmallAllocationThreshold = 256 << 10;
public:
DxvkMemoryAllocator(const DxvkDevice* device);
@@ -369,16 +389,15 @@ namespace dxvk {
* \param [in] dedAllocReq Dedicated allocation requirements
* \param [in] dedAllocInfo Dedicated allocation info
* \param [in] flags Memory type flags
- * \param [in] priority Device-local memory priority
+ * \param [in] hints Memory hints
* \returns Allocated memory slice
*/
DxvkMemory alloc(
const VkMemoryRequirements* req,
const VkMemoryDedicatedRequirements& dedAllocReq,
const VkMemoryDedicatedAllocateInfo& dedAllocInfo,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- float priority,
+ VkMemoryPropertyFlags flags,
+ DxvkMemoryFlags hints,
DxvkMemoryStats::Category category);
/**
@@ -434,31 +453,28 @@ namespace dxvk {
std::array m_memTypes;
DxvkMemory tryAlloc(
- const VkMemoryRequirements* req,
- const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- float priority,
- DxvkMemoryStats::Category category);
+ const VkMemoryRequirements* req,
+ const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
+ VkMemoryPropertyFlags flags,
+ DxvkMemoryFlags hints,
+ DxvkMemoryStats::Category category);
DxvkMemory tryAllocFromType(
- DxvkMemoryType* type,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- VkDeviceSize size,
- VkDeviceSize align,
- float priority,
- const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
- DxvkMemoryStats::Category category);
+ DxvkMemoryType* type,
+ VkMemoryPropertyFlags flags,
+ VkDeviceSize size,
+ VkDeviceSize align,
+ DxvkMemoryFlags hints,
+ const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
+ DxvkMemoryStats::Category category);
DxvkDeviceMemory tryAllocDeviceMemory(
- DxvkMemoryType* type,
- VkMemoryPropertyFlags propertyFlags,
- VkMemoryAllocateFlags allocateFlags,
- VkDeviceSize size,
- float priority,
- const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
- DxvkMemoryStats::Category category);
+ DxvkMemoryType* type,
+ VkMemoryPropertyFlags flags,
+ VkDeviceSize size,
+ DxvkMemoryFlags hints,
+ const VkMemoryDedicatedAllocateInfo* dedAllocInfo,
+ DxvkMemoryStats::Category category);
void free(
const DxvkMemory& memory);
@@ -474,7 +490,19 @@ namespace dxvk {
DxvkDeviceMemory memory);
VkDeviceSize pickChunkSize(
- uint32_t memTypeId) const;
+ uint32_t memTypeId,
+ DxvkMemoryFlags hints) const;
+
+ bool shouldFreeChunk(
+ const DxvkMemoryType* type,
+ const Rc& chunk) const;
+
+ bool shouldFreeEmptyChunks(
+ const DxvkMemoryHeap* heap,
+ VkDeviceSize allocationSize) const;
+
+ void freeEmptyChunks(
+ const DxvkMemoryHeap* heap);
};
diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp
index f05179e15..565a296dc 100644
--- a/src/dxvk/dxvk_options.cpp
+++ b/src/dxvk/dxvk_options.cpp
@@ -26,8 +26,6 @@ namespace dxvk {
DxvkOptions::DxvkOptions(const Config& config) {
enableStateCache = config.getOption ("dxvk.enableStateCache", true);
- enableOpenVR = config.getOption ("dxvk.enableOpenVR", true);
- enableOpenXR = config.getOption ("dxvk.enableOpenXR", true);
numCompilerThreads = config.getOption ("dxvk.numCompilerThreads", 0);
useRawSsbo = config.getOption("dxvk.useRawSsbo", Tristate::Auto);
shrinkNvidiaHvvHeap = config.getOption("dxvk.shrinkNvidiaHvvHeap", Tristate::Auto);
diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h
index b45eceea3..478b32aac 100644
--- a/src/dxvk/dxvk_options.h
+++ b/src/dxvk/dxvk_options.h
@@ -32,12 +32,6 @@ namespace dxvk {
/// Enable state cache
bool enableStateCache;
- /// Enables OpenVR loading
- bool enableOpenVR;
-
- /// Enables OpenXR loading
- bool enableOpenXR;
-
/// Number of compiler threads
/// when using the state cache
int32_t numCompilerThreads;
diff --git a/src/dxvk/dxvk_stats.h b/src/dxvk/dxvk_stats.h
index bf556bbd1..6c900ba97 100644
--- a/src/dxvk/dxvk_stats.h
+++ b/src/dxvk/dxvk_stats.h
@@ -11,16 +11,21 @@ namespace dxvk {
* thogether with \ref DxvkStatCounters.
*/
enum class DxvkStatCounter : uint32_t {
- CmdDrawCalls, ///< Number of draw calls
- CmdDispatchCalls, ///< Number of compute calls
+ CmdDrawCalls, ///< Number of draw calls
+ CmdDispatchCalls, ///< Number of compute calls
+ CmdRenderPassCount, ///< Number of render passes
+ PipeCountGraphics, ///< Number of graphics pipelines
+ PipeCountCompute, ///< Number of compute pipelines
+ PipeCompilerBusy, ///< Boolean indicating compiler activity
+ QueueSubmitCount, ///< Number of command buffer submissions
+ QueuePresentCount, ///< Number of present calls / frames
+ GpuIdleTicks, ///< GPU idle time in microseconds
+ CsSyncCount, ///< CS thread synchronizations
+ CsSyncTicks, ///< Time spent waiting on CS
+ CsChunkCount, ///< Submitted CS chunks
+
+ // NV-DXVK begin: RTX Remix counters
CmdTraceRaysCalls, ///< Number of traceRays calls
- CmdRenderPassCount, ///< Number of render passes
- PipeCountGraphics, ///< Number of graphics pipelines
- PipeCountCompute, ///< Number of compute pipelines
- PipeCompilerBusy, ///< Boolean indicating compiler activity
- QueueSubmitCount, ///< Number of command buffer submissions
- QueuePresentCount, ///< Number of present calls / frames
- GpuIdleTicks, ///< GPU idle time in microseconds
RtxBlasCount, ///< Number of unique BLAS's in the scene/geometry cache
RtxBufferCount, ///< Number of unique buffers being tracked for RT rendering
RtxTextureCount, ///< Number of unique textures being tracked for RT rendering
@@ -32,7 +37,9 @@ namespace dxvk {
RtxSamplers, ///< Number of samplers currently present in the scene
RtxTexturesInFlight, ///< Number of texture currently being loaded
RtxLastTextureBatchDuration, ///< Duration in ms of the last processed texture batch
- NumCounters, ///< Number of counters available
+ // NV-DXVK end
+
+ NumCounters, ///< Number of counters available
};
diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp
index 5b4490f05..c64632f91 100644
--- a/src/dxvk/hud/dxvk_hud.cpp
+++ b/src/dxvk/hud/dxvk_hud.cpp
@@ -45,6 +45,7 @@ namespace dxvk::hud {
addItem("pipelines", -1, device);
addItem("memory", -1, device);
addItem("raytracingMode", -1);
+ addItem("cs", -1, device);
addItem("gpuload", -1, device);
addItem("compiler", -1, device);
addItem("rtx", -1, device);
diff --git a/src/dxvk/hud/dxvk_hud_item.cpp b/src/dxvk/hud/dxvk_hud_item.cpp
index 1873a9728..8fb09117b 100644
--- a/src/dxvk/hud/dxvk_hud_item.cpp
+++ b/src/dxvk/hud/dxvk_hud_item.cpp
@@ -570,8 +570,8 @@ namespace dxvk::hud {
bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
VkDeviceSize memSizeMib = m_memory.memoryHeaps[i].size >> 20;
- VkDeviceSize memAllocatedMib = m_heaps[i].totalAllocated() >> 20;
VkDeviceSize memUsedMib = m_heaps[i].totalUsed() >> 20;
+ VkDeviceSize memAllocatedMib = m_heaps[i].totalAllocated() >> 20;
uint64_t percentage = (100 * memUsedMib) / memSizeMib;
std::string label = str::format(isDeviceLocal ? "Vidmem" : "Sysmem", " heap ", i, ":");
@@ -614,6 +614,85 @@ namespace dxvk::hud {
}
+ HudCsThreadItem::HudCsThreadItem(const Rc& device)
+ : m_device(device) {
+
+ }
+
+
+ HudCsThreadItem::~HudCsThreadItem() {
+
+ }
+
+
+ void HudCsThreadItem::update(dxvk::high_resolution_clock::time_point time) {
+ uint64_t ticks = std::chrono::duration_cast(time - m_lastUpdate).count();
+
+ // Capture the maximum here since it's more useful to
+ // identify stutters than using any sort of average
+ DxvkStatCounters counters = m_device->getStatCounters();
+ uint64_t currCsSyncCount = counters.getCtr(DxvkStatCounter::CsSyncCount);
+ uint64_t currCsSyncTicks = counters.getCtr(DxvkStatCounter::CsSyncTicks);
+
+ m_maxCsSyncCount = std::max(m_maxCsSyncCount, currCsSyncCount - m_prevCsSyncCount);
+ m_maxCsSyncTicks = std::max(m_maxCsSyncTicks, currCsSyncTicks - m_prevCsSyncTicks);
+
+ m_prevCsSyncCount = currCsSyncCount;
+ m_prevCsSyncTicks = currCsSyncTicks;
+
+ m_updateCount++;
+
+ if (ticks >= UpdateInterval) {
+ uint64_t currCsChunks = counters.getCtr(DxvkStatCounter::CsChunkCount);
+ uint64_t diffCsChunks = (currCsChunks - m_prevCsChunks) / m_updateCount;
+ m_prevCsChunks = currCsChunks;
+
+ uint64_t syncTicks = m_maxCsSyncTicks / 100;
+
+ m_csChunkString = str::format(diffCsChunks);
+ m_csSyncString = m_maxCsSyncCount
+ ? str::format(m_maxCsSyncCount, " (", (syncTicks / 10), ".", (syncTicks % 10), " ms)")
+ : str::format(m_maxCsSyncCount);
+
+ m_maxCsSyncCount = 0;
+ m_maxCsSyncTicks = 0;
+
+ m_updateCount = 0;
+ m_lastUpdate = time;
+ }
+ }
+
+
+ HudPos HudCsThreadItem::render(
+ HudRenderer& renderer,
+ HudPos position) {
+ position.y += 16.0f;
+ renderer.drawText(16.0f,
+ { position.x, position.y },
+ { 0.25f, 1.0f, 0.25f, 1.0f },
+ "CS chunks:");
+
+ renderer.drawText(16.0f,
+ { position.x + 132.0f, position.y },
+ { 1.0f, 1.0f, 1.0f, 1.0f },
+ m_csChunkString);
+
+ position.y += 20.0f;
+ renderer.drawText(16.0f,
+ { position.x, position.y },
+ { 0.25f, 1.0f, 0.25f, 1.0f },
+ "CS syncs:");
+
+ renderer.drawText(16.0f,
+ { position.x + 132.0f, position.y },
+ { 1.0f, 1.0f, 1.0f, 1.0f },
+ m_csSyncString);
+
+ position.y += 8.0f;
+ return position;
+ }
+
+
HudGpuLoadItem::HudGpuLoadItem(const Rc& device)
: m_device(device) {
diff --git a/src/dxvk/hud/dxvk_hud_item.h b/src/dxvk/hud/dxvk_hud_item.h
index bafd3b7f9..963841e96 100644
--- a/src/dxvk/hud/dxvk_hud_item.h
+++ b/src/dxvk/hud/dxvk_hud_item.h
@@ -383,6 +383,45 @@ namespace dxvk::hud {
};
+ /**
+ * \brief HUD item to display CS thread statistics
+ */
+ class HudCsThreadItem : public HudItem {
+ constexpr static int64_t UpdateInterval = 500'000;
+ public:
+
+ HudCsThreadItem(const Rc& device);
+
+ ~HudCsThreadItem();
+
+ void update(dxvk::high_resolution_clock::time_point time);
+
+ HudPos render(
+ HudRenderer& renderer,
+ HudPos position);
+
+ private:
+
+ Rc m_device;
+
+ uint64_t m_prevCsSyncCount = 0;
+ uint64_t m_prevCsSyncTicks = 0;
+ uint64_t m_prevCsChunks = 0;
+
+ uint64_t m_maxCsSyncCount = 0;
+ uint64_t m_maxCsSyncTicks = 0;
+
+ uint64_t m_updateCount = 0;
+
+ std::string m_csSyncString;
+ std::string m_csChunkString;
+
+ dxvk::high_resolution_clock::time_point m_lastUpdate
+ = dxvk::high_resolution_clock::now();
+
+ };
+
+
/**
* \brief HUD item to display GPU load
*/
diff --git a/src/dxvk/imgui/dxvk_imgui.cpp b/src/dxvk/imgui/dxvk_imgui.cpp
index cf497aad9..3abedbfa7 100644
--- a/src/dxvk/imgui/dxvk_imgui.cpp
+++ b/src/dxvk/imgui/dxvk_imgui.cpp
@@ -2600,7 +2600,7 @@ namespace dxvk {
ImGui::Checkbox("Enable Volumetric Lighting", &RtxOptions::Get()->enableVolumetricLightingObject());
if (RtxOptions::Get()->enableVolumetricLighting()) {
- ImGui::DragFloat3("Transmittance Color", &RtxOptions::Get()->volumetricTransmittanceColorObject(), 0.01f, 0.0f, 1.0f, "%.3f");
+ ImGui::DragFloat3("Transmittance Color", &RtxOptions::Get()->volumetricTransmittanceColorObject(), 0.01f, 0.0f, VolumeManager::MaxTransmittanceValue, "%.3f");
ImGui::DragFloat("Transmittance Measurement Distance", &RtxOptions::Get()->volumetricTransmittanceMeasurementDistanceObject(), 0.25f, 0.0f, FLT_MAX, "%.2f", sliderFlags);
ImGui::DragFloat3("Single Scattering Albedo", &RtxOptions::Get()->volumetricSingleScatteringAlbedoObject(), 0.01f, 0.0f, 1.0f, "%.3f");
ImGui::DragFloat("Anisotropy", &RtxOptions::Get()->volumetricAnisotropyObject(), 0.01f, -1.0f, 1.0f, "%.3f", sliderFlags);
@@ -2647,6 +2647,14 @@ namespace dxvk {
ImGui::Checkbox("Enable Thin Opaque", &RtxOptions::SubsurfaceScattering::enableThinOpaqueObject());
+ if (RtxOptions::SubsurfaceScattering::enableThinOpaque()) {
+ ImGui::Indent();
+
+ ImGui::Checkbox("Enable Texture Maps", &RtxOptions::SubsurfaceScattering::enableTextureMapsObject());
+
+ ImGui::Unindent();
+ }
+
ImGui::Unindent();
}
diff --git a/src/dxvk/imgui/dxvk_imgui_capture.cpp b/src/dxvk/imgui/dxvk_imgui_capture.cpp
index 4d01c7397..6c415fb52 100644
--- a/src/dxvk/imgui/dxvk_imgui_capture.cpp
+++ b/src/dxvk/imgui/dxvk_imgui_capture.cpp
@@ -54,6 +54,7 @@ namespace dxvk {
}
void ImGuiCapture::show(const Rc& ctx) {
+ auto capturer = ctx->getCommonObjects()->capturer();
const bool disableCapture =
ctx->getCommonObjects()->getSceneManager().areReplacementsLoaded() &&
RtxOptions::Get()->getEnableAnyReplacements();
@@ -68,6 +69,7 @@ namespace dxvk {
showContinuousCapture(ctx);
}
ImGui::Separator();
+ ImGui::Checkbox("Correct baked world transforms", &capturer->correctBakedTransformsRef());
ImGui::Checkbox("Show menu on capture hotkey", &RtxOptions::Get()->m_captureShowMenuOnHotkey);
if(RtxOptions::Get()->m_captureShowMenuOnHotkey) {
ImGui::PushTextWrapPos(ImGui::GetCurrentWindow()->Size.x);
diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build
index b6bb312c9..c10f47576 100644
--- a/src/dxvk/meson.build
+++ b/src/dxvk/meson.build
@@ -215,6 +215,7 @@ dxvk_src = files([
'rtx_render/rtx_game_capturer.cpp',
'rtx_render/rtx_game_capturer.h',
'rtx_render/rtx_game_capturer_paths.h',
+ 'rtx_render/rtx_game_capturer_utils.h',
'rtx_render/rtx_geometry_utils.cpp',
'rtx_render/rtx_geometry_utils.h',
'rtx_render/rtx_hashing.cpp',
@@ -280,6 +281,7 @@ dxvk_src = files([
'rtx_render/rtx_ray_portal_manager.h',
'rtx_render/rtx_reflex.cpp',
'rtx_render/rtx_reflex.h',
+ 'rtx_render/rtx_remix_api.cpp',
'rtx_render/rtx_resources.cpp',
'rtx_render/rtx_resources.h',
'rtx_render/rtx_restir_gi_rayquery.cpp',
@@ -424,7 +426,7 @@ endif
dxvk_lib = static_library('dxvk', dxvk_src, dxvk_version, [generated_dxvk_shaders, rtx_shaders, dxvk_generated_embedded_files],
link_with : [ util_lib, spirv_lib ],
dependencies : [ dxvk_deps ],
- include_directories : [ dxvk_include_path, dxvk_shader_include_path, rtxdi_include_path ],
+ include_directories : [ dxvk_include_path, dxvk_shader_include_path, rtxdi_include_path, remix_api_include_path ],
override_options : ['cpp_std='+dxvk_cpp_std])
dxvk_dep = declare_dependency(
diff --git a/src/dxvk/rtx_render/rtx_accel_manager.cpp b/src/dxvk/rtx_render/rtx_accel_manager.cpp
index f4c79ae67..421cbb495 100644
--- a/src/dxvk/rtx_render/rtx_accel_manager.cpp
+++ b/src/dxvk/rtx_render/rtx_accel_manager.cpp
@@ -222,6 +222,10 @@ namespace dxvk {
}
}
+ int AccelManager::getCurrentFramePrimitiveIDPrefixSumBufferID() const {
+ return m_device->getCurrentFrameId() & 0x1;
+ }
+
void AccelManager::createAndBuildIntersectionBlas(Rc ctx, DxvkBarrierSet& execBarriers) {
if (m_intersectionBlas.ptr())
return;
@@ -568,7 +572,7 @@ namespace dxvk {
// Copy the instance transform data to the device
if(instanceTransforms.size() > 0)
- ctx->updateBuffer(m_transformBuffer, 0, instanceTransforms.size() * sizeof(VkTransformMatrixKHR), instanceTransforms.data());
+ ctx->writeToBuffer(m_transformBuffer, 0, instanceTransforms.size() * sizeof(VkTransformMatrixKHR), instanceTransforms.data());
ctx->getCommandList()->trackResource(m_transformBuffer);
ctx->getCommandList()->trackResource(m_transformBuffer);
@@ -595,6 +599,7 @@ namespace dxvk {
// Build prefix sum array
// Collect primitive count for each surface object
+ m_reorderedSurfacesPrimitiveIDPrefixSumLastFrame = m_reorderedSurfacesPrimitiveIDPrefixSum;
m_reorderedSurfacesPrimitiveIDPrefixSum.resize(m_reorderedSurfaces.size());
for (uint32_t i = 0; i < m_reorderedSurfaces.size(); i++) {
auto surface = m_reorderedSurfaces[i];
@@ -810,7 +815,7 @@ namespace dxvk {
for (const auto& instances : m_mergedInstances) {
if (!instances.empty()) {
const size_t size = instances.size() * sizeof(VkAccelerationStructureInstanceKHR);
- ctx->updateBuffer(m_vkInstanceBuffer, offset, size, instances.data());
+ ctx->writeToBuffer(m_vkInstanceBuffer, offset, size, instances.data());
offset += size;
}
}
@@ -823,7 +828,7 @@ namespace dxvk {
}
// Write billboard data
- ctx->updateBuffer(m_billboardsBuffer, 0, numActiveBillboards * sizeof(MemoryBillboard), memoryBillboards.data());
+ ctx->writeToBuffer(m_billboardsBuffer, 0, numActiveBillboards * sizeof(MemoryBillboard), memoryBillboards.data());
}
}
@@ -862,7 +867,7 @@ namespace dxvk {
assert(dataOffset == surfacesGPUSize);
assert(surfacesGPUData.size() == surfacesGPUSize);
- ctx->updateBuffer(m_surfaceBuffer, 0, surfacesGPUData.size(), surfacesGPUData.data());
+ ctx->writeToBuffer(m_surfaceBuffer, 0, surfacesGPUData.size(), surfacesGPUData.data());
// Find the size of the surface mapping buffer
uint32_t maxPreviousSurfaceIndex = 0;
@@ -894,11 +899,20 @@ namespace dxvk {
}
// Create and upload the primitive id prefix sum buffer
- info.size = align(m_reorderedSurfacesPrimitiveIDPrefixSum.size() * sizeof(m_reorderedSurfacesPrimitiveIDPrefixSum[0]), kBufferAlignment);
- if (m_primitiveIDPrefixSumBuffer == nullptr || info.size > m_primitiveIDPrefixSumBuffer->info().size) {
- m_primitiveIDPrefixSumBuffer = m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, DxvkMemoryStats::Category::RTXAccelerationStructure);
- }
- ctx->updateBuffer(m_primitiveIDPrefixSumBuffer, 0, m_reorderedSurfacesPrimitiveIDPrefixSum.size() * sizeof(m_reorderedSurfacesPrimitiveIDPrefixSum[0]), m_reorderedSurfacesPrimitiveIDPrefixSum.data());
+ auto updatePrefixSumBuffer = [&info, this, ctx](std::vector& prefixSumList, Rc& prefixSumBuffer) {
+ info.size = std::max(prefixSumList.size(), 1llu) * sizeof(prefixSumList[0]);
+
+ if (prefixSumBuffer == nullptr || info.size > prefixSumBuffer->info().size) {
+ prefixSumBuffer = m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, DxvkMemoryStats::Category::RTXAccelerationStructure);
+ }
+
+ if (prefixSumList.size() > 0) {
+ ctx->writeToBuffer(prefixSumBuffer, 0, prefixSumList.size() * sizeof(prefixSumList[0]), prefixSumList.data());
+ }
+ };
+
+ updatePrefixSumBuffer(m_reorderedSurfacesPrimitiveIDPrefixSum, m_primitiveIDPrefixSumBuffer);
+ updatePrefixSumBuffer(m_reorderedSurfacesPrimitiveIDPrefixSumLastFrame, m_primitiveIDPrefixSumBufferLastFrame);
// Create and upload the surface mapping buffer
if (!surfaceIndexMapping.empty()) {
@@ -907,7 +921,7 @@ namespace dxvk {
m_surfaceMappingBuffer = m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, DxvkMemoryStats::Category::RTXAccelerationStructure);
}
- ctx->updateBuffer(m_surfaceMappingBuffer, 0, surfaceIndexMapping.size() * sizeof(surfaceIndexMapping[0]), surfaceIndexMapping.data());
+ ctx->writeToBuffer(m_surfaceMappingBuffer, 0, surfaceIndexMapping.size() * sizeof(surfaceIndexMapping[0]), surfaceIndexMapping.data());
}
}
diff --git a/src/dxvk/rtx_render/rtx_accel_manager.h b/src/dxvk/rtx_render/rtx_accel_manager.h
index 858bd83d4..f9e2b9ea7 100644
--- a/src/dxvk/rtx_render/rtx_accel_manager.h
+++ b/src/dxvk/rtx_render/rtx_accel_manager.h
@@ -78,7 +78,13 @@ class AccelManager : public CommonDeviceObject {
const Rc getSurfaceMappingBuffer() const { return m_surfaceMappingBuffer; }
- const Rc getPrimitiveIDPrefixSumBuffer() const { return m_primitiveIDPrefixSumBuffer; }
+ const Rc getCurrentFramePrimitiveIDPrefixSumBuffer() const {
+ return m_primitiveIDPrefixSumBuffer;
+ }
+
+ const Rc getLastFramePrimitiveIDPrefixSumBuffer() const {
+ return m_primitiveIDPrefixSumBufferLastFrame;
+ }
const Rc getBillboardsBuffer() const { return m_billboardsBuffer; }
@@ -123,6 +129,7 @@ class AccelManager : public CommonDeviceObject {
std::vector m_reorderedSurfaces;
std::vector m_reorderedSurfacesFirstIndexOffset;
std::vector m_reorderedSurfacesPrimitiveIDPrefixSum;
+ std::vector m_reorderedSurfacesPrimitiveIDPrefixSumLastFrame;
std::vector m_mergedInstances[Tlas::Count];
std::vector> m_blasPool;
@@ -131,6 +138,9 @@ class AccelManager : public CommonDeviceObject {
Rc m_surfaceMappingBuffer;
Rc m_transformBuffer;
Rc m_primitiveIDPrefixSumBuffer;
+ Rc m_primitiveIDPrefixSumBufferLastFrame;
+
+ int getCurrentFramePrimitiveIDPrefixSumBufferID() const;
Rc m_intersectionBlas;
Rc m_aabbBuffer;
diff --git a/src/dxvk/rtx_render/rtx_asset_replacer.cpp b/src/dxvk/rtx_render/rtx_asset_replacer.cpp
index 216a435ee..2091eaf51 100644
--- a/src/dxvk/rtx_render/rtx_asset_replacer.cpp
+++ b/src/dxvk/rtx_render/rtx_asset_replacer.cpp
@@ -19,7 +19,6 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
-#pragma once
#include "rtx_asset_replacer.h"
@@ -162,5 +161,59 @@ void AssetReplacer::updateSecretReplacements() {
m_bSecretReplacementsUpdated = updated;
}
+namespace {
+ std::string tostr(const remixapi_MaterialHandle& h) {
+ static_assert(sizeof h == sizeof uint64_t);
+ return std::to_string(reinterpret_cast(h));
+ }
+ std::string tostr(const remixapi_MeshHandle& h) {
+ static_assert(sizeof h == sizeof uint64_t);
+ return std::to_string(reinterpret_cast(h));
+ }
+}
+
+void AssetReplacer::makeMaterialWithTexturePreload(DxvkContext& ctx, remixapi_MaterialHandle handle, MaterialData&& data) {
+ auto [iter, isNew] = m_extMaterials.emplace(handle, std::move(data));
+
+ if (!isNew) {
+ Logger::info("Ignoring repeated material registration (handle=" + tostr(handle) + ") ");
+ return;
+ }
+}
+
+const MaterialData* AssetReplacer::accessExternalMaterial(remixapi_MaterialHandle handle) const {
+ auto found = m_extMaterials.find(handle);
+ if (found == m_extMaterials.end()) {
+ return nullptr;
+ }
+ return found->second ? &found->second.value() : nullptr;
+}
+
+void AssetReplacer::destroyExternalMaterial(remixapi_MaterialHandle handle) {
+ m_extMaterials.erase(handle);
+}
+
+void AssetReplacer::registerExternalMesh(remixapi_MeshHandle handle, std::vector&& submeshes) {
+ if (m_extMeshes.count(handle) > 0) {
+ Logger::info("Ignoring repeated mesh registration (handle=" + tostr(handle) + ") ");
+ return;
+ }
+
+ m_extMeshes.emplace(handle, std::move(submeshes));
+}
+
+const std::vector& AssetReplacer::accessExternalMesh(remixapi_MeshHandle handle) const {
+ auto found = m_extMeshes.find(handle);
+ if (found == m_extMeshes.end()) {
+ static const auto s_empty = std::vector {};
+ return s_empty;
+ }
+ return found->second;
+}
+
+void AssetReplacer::destroyExternalMesh(remixapi_MeshHandle handle) {
+ m_extMeshes.erase(handle);
+}
+
} // namespace dxvk
diff --git a/src/dxvk/rtx_render/rtx_asset_replacer.h b/src/dxvk/rtx_render/rtx_asset_replacer.h
index c3f94e0a8..742ecd7db 100644
--- a/src/dxvk/rtx_render/rtx_asset_replacer.h
+++ b/src/dxvk/rtx_render/rtx_asset_replacer.h
@@ -217,6 +217,14 @@ namespace dxvk {
(bEnabled) ? variantId : VariantInfo::kDefaultVariant;
}
+ void makeMaterialWithTexturePreload(DxvkContext& ctx, remixapi_MaterialHandle handle, MaterialData&& data);
+ [[nodiscard]] const MaterialData* accessExternalMaterial(remixapi_MaterialHandle handle) const;
+ void destroyExternalMaterial(remixapi_MaterialHandle handle);
+
+ void registerExternalMesh(remixapi_MeshHandle handle, std::vector&& submeshes);
+ [[nodiscard]] const std::vector& accessExternalMesh(remixapi_MeshHandle handle) const;
+ void destroyExternalMesh(remixapi_MeshHandle handle);
+
private:
void updateSecretReplacements();
@@ -232,6 +240,9 @@ namespace dxvk {
SecretReplacements m_secretReplacements;
ModManager m_modManager;
+
+ std::unordered_map> m_extMaterials {};
+ std::unordered_map> m_extMeshes {};
};
} // namespace dxvk
diff --git a/src/dxvk/rtx_render/rtx_bindless_resource_manager.cpp b/src/dxvk/rtx_render/rtx_bindless_resource_manager.cpp
index 8a598740b..ea2f6475f 100644
--- a/src/dxvk/rtx_render/rtx_bindless_resource_manager.cpp
+++ b/src/dxvk/rtx_render/rtx_bindless_resource_manager.cpp
@@ -86,18 +86,20 @@ namespace dxvk {
assert(idx <= kMaxBindlessResources);
- VkWriteDescriptorSet descWrites;
- descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- descWrites.pNext = nullptr;
- descWrites.dstSet = 0;// This will be filled in by the BindlessTable
- descWrites.dstBinding = 0;
- descWrites.dstArrayElement = 0;
- descWrites.descriptorCount = idx;
- descWrites.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- descWrites.pImageInfo = &imageInfo[0];
- descWrites.pBufferInfo = nullptr;
- descWrites.pTexelBufferView = nullptr;
- m_tables[Table::Textures][currentIdx()]->updateDescriptors(descWrites);
+ if (idx > 0) {
+ VkWriteDescriptorSet descWrites;
+ descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrites.pNext = nullptr;
+ descWrites.dstSet = 0;// This will be filled in by the BindlessTable
+ descWrites.dstBinding = 0;
+ descWrites.dstArrayElement = 0;
+ descWrites.descriptorCount = idx;
+ descWrites.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ descWrites.pImageInfo = &imageInfo[0];
+ descWrites.pBufferInfo = nullptr;
+ descWrites.pTexelBufferView = nullptr;
+ m_tables[Table::Textures][currentIdx()]->updateDescriptors(descWrites);
+ }
}
// Buffers
@@ -117,18 +119,20 @@ namespace dxvk {
assert(idx <= kMaxBindlessResources);
- VkWriteDescriptorSet descWrites;
- descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- descWrites.pNext = nullptr;
- descWrites.dstSet = 0;// This will be filled in by the BindlessTable
- descWrites.dstBinding = 0;
- descWrites.dstArrayElement = 0;
- descWrites.descriptorCount = idx;
- descWrites.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
- descWrites.pImageInfo = nullptr;
- descWrites.pBufferInfo = &bufferInfo[0];
- descWrites.pTexelBufferView = nullptr;
- m_tables[Table::Buffers][currentIdx()]->updateDescriptors(descWrites);
+ if (idx > 0) {
+ VkWriteDescriptorSet descWrites;
+ descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrites.pNext = nullptr;
+ descWrites.dstSet = 0;// This will be filled in by the BindlessTable
+ descWrites.dstBinding = 0;
+ descWrites.dstArrayElement = 0;
+ descWrites.descriptorCount = idx;
+ descWrites.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ descWrites.pImageInfo = nullptr;
+ descWrites.pBufferInfo = &bufferInfo[0];
+ descWrites.pTexelBufferView = nullptr;
+ m_tables[Table::Buffers][currentIdx()]->updateDescriptors(descWrites);
+ }
}
// Samplers
@@ -150,18 +154,20 @@ namespace dxvk {
assert(idx <= kMaxBindlessSamplers);
- VkWriteDescriptorSet descWrites;
- descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- descWrites.pNext = nullptr;
- descWrites.dstSet = 0;// This will be filled in by the BindlessTable
- descWrites.dstBinding = 0;
- descWrites.dstArrayElement = 0;
- descWrites.descriptorCount = idx;
- descWrites.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
- descWrites.pImageInfo = &imageInfo[0];
- descWrites.pBufferInfo = nullptr;
- descWrites.pTexelBufferView = nullptr;
- m_tables[Table::Samplers][currentIdx()]->updateDescriptors(descWrites);
+ if (idx > 0) {
+ VkWriteDescriptorSet descWrites;
+ descWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrites.pNext = nullptr;
+ descWrites.dstSet = 0;// This will be filled in by the BindlessTable
+ descWrites.dstBinding = 0;
+ descWrites.dstArrayElement = 0;
+ descWrites.descriptorCount = idx;
+ descWrites.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
+ descWrites.pImageInfo = &imageInfo[0];
+ descWrites.pBufferInfo = nullptr;
+ descWrites.pTexelBufferView = nullptr;
+ m_tables[Table::Samplers][currentIdx()]->updateDescriptors(descWrites);
+ }
}
m_frameLastUpdated = m_device->getCurrentFrameId();
diff --git a/src/dxvk/rtx_render/rtx_camera_manager.cpp b/src/dxvk/rtx_render/rtx_camera_manager.cpp
index bf2c916e4..379b327fe 100644
--- a/src/dxvk/rtx_render/rtx_camera_manager.cpp
+++ b/src/dxvk/rtx_render/rtx_camera_manager.cpp
@@ -256,4 +256,16 @@ namespace dxvk {
return m_lastCameraCutFrameId == m_device->getCurrentFrameId();
}
+ void CameraManager::processExternalCamera(CameraType::Enum type,
+ const Matrix4& worldToView,
+ const Matrix4& viewToProjection) {
+ float fov, aspectRatio, nearPlane, farPlane, shearX, shearY;
+ bool isLHS;
+ bool isReverseZ;
+ decomposeProjection(viewToProjection, aspectRatio, fov, nearPlane, farPlane, shearX, shearY, isLHS, isReverseZ);
+
+ getCamera(type).update(
+ m_device->getCurrentFrameId(),
+ worldToView, viewToProjection, fov, aspectRatio, nearPlane, farPlane, isLHS);
+ }
} // namespace dxvk
diff --git a/src/dxvk/rtx_render/rtx_camera_manager.h b/src/dxvk/rtx_render/rtx_camera_manager.h
index f52f95ad7..7c8e69d33 100644
--- a/src/dxvk/rtx_render/rtx_camera_manager.h
+++ b/src/dxvk/rtx_render/rtx_camera_manager.h
@@ -55,6 +55,7 @@ namespace dxvk {
// Calculates a camera type for the specified draw call.
CameraType::Enum processCameraData(const DrawCallState& input);
+ void processExternalCamera(CameraType::Enum type, const Matrix4& worldToView, const Matrix4& viewToProjection);
uint32_t getLastCameraCutFrameId() const { return m_lastCameraCutFrameId; }
bool isCameraCutThisFrame() const;
diff --git a/src/dxvk/rtx_render/rtx_composite.cpp b/src/dxvk/rtx_render/rtx_composite.cpp
index fd6313ee5..d6a7937fc 100644
--- a/src/dxvk/rtx_render/rtx_composite.cpp
+++ b/src/dxvk/rtx_render/rtx_composite.cpp
@@ -348,7 +348,7 @@ namespace dxvk {
compositeArgs.stochasticAlphaBlendRadianceVolumeMultiplier = stochasticAlphaBlendRadianceVolumeMultiplier();
Rc cb = getCompositeConstantsBuffer();
- ctx->updateBuffer(cb, 0, sizeof(CompositeArgs), &compositeArgs);
+ ctx->writeToBuffer(cb, 0, sizeof(CompositeArgs), &compositeArgs);
ctx->getCommandList()->trackResource(cb);
ctx->bindResourceBuffer(COMPOSITE_CONSTANTS_INPUT, DxvkBufferSlice(cb, 0, cb->info().size));
diff --git a/src/dxvk/rtx_render/rtx_context.cpp b/src/dxvk/rtx_render/rtx_context.cpp
index d34d30eef..545ae1f9c 100644
--- a/src/dxvk/rtx_render/rtx_context.cpp
+++ b/src/dxvk/rtx_render/rtx_context.cpp
@@ -67,6 +67,8 @@ namespace dxvk {
Metrics Metrics::s_instance;
+ bool g_allowSrgbConversionForOutput = true;
+
void RtxContext::takeScreenshot(std::string imageName, Rc image) {
// NOTE: Improve this, I'd like all textures from the same frame to have the same time code... Currently sampling the time on each "dump op" results in different timecodes.
auto t = std::time(nullptr);
@@ -513,7 +515,7 @@ namespace dxvk {
// Tone mapping
// WAR for TREX-553 - disable sRGB conversion as NVTT implicitly applies it during dds->png
// conversion for 16bit float formats
- const bool performSRGBConversion = !captureScreenImage;
+ const bool performSRGBConversion = !captureScreenImage && g_allowSrgbConversionForOutput;
dispatchToneMapping(rtOutput, performSRGBConversion, frameTimeSecs);
if (captureScreenImage) {
@@ -694,6 +696,10 @@ namespace dxvk {
}
}
+ void RtxContext::commitExternalGeometryToRT(ExternalDrawState&& state) {
+ getSceneManager().submitExternalDraw(this, std::move(state));
+ }
+
static uint32_t jenkinsHash(uint32_t a) {
// http://burtleburtle.net/bob/hash/integer.html
a = (a + 0x7ed55d16) + (a << 12);
@@ -850,6 +856,11 @@ namespace dxvk {
constants.totalMipBias = getSceneManager().getTotalMipBias();
+ const VkExtent3D& rtExtent = rtOutput.m_finalOutput.image->info().extent;
+ constants.upscaleFactor = float2 {
+ rtOutput.m_compositeOutputExtent.width / static_cast(rtExtent.width),
+ rtOutput.m_compositeOutputExtent.height / static_cast(rtExtent.height) };
+
constants.terrainArgs = getSceneManager().getTerrainBaker().getTerrainArgs();
constants.thinOpaqueEnable = RtxOptions::SubsurfaceScattering::enableThinOpaque();
@@ -887,7 +898,7 @@ namespace dxvk {
constants.enableReSTIRGIDemodulatedTargetFunction = restirGI.useDemodulatedTargetFunction();
- m_common->metaNeeCache().setRaytraceArgs(constants);
+ m_common->metaNeeCache().setRaytraceArgs(constants, m_resetHistory);
constants.surfaceCount = getSceneManager().getAccelManager().getSurfaceCount();
auto* cameraTeleportDirectionInfo = getSceneManager().getRayPortalManager().getCameraTeleportationRayPortalDirectionInfo();
@@ -984,11 +995,13 @@ namespace dxvk {
constants.isZUp = RtxOptions::Get()->isZUp();
constants.enableCullingSecondaryRays = RtxOptions::Get()->enableCullingInSecondaryRays();
+ constants.domeLightArgs = getSceneManager().getLightManager().getDomeLightArgs();
+
// Upload the constants to the GPU
{
Rc cb = getResourceManager().getConstantsBuffer();
- updateBuffer(cb, 0, sizeof(constants), &constants);
+ writeToBuffer(cb, 0, sizeof(constants), &constants);
m_cmd->trackResource(cb);
}
diff --git a/src/dxvk/rtx_render/rtx_context.h b/src/dxvk/rtx_render/rtx_context.h
index 6ce4c3ca3..01a4838db 100644
--- a/src/dxvk/rtx_render/rtx_context.h
+++ b/src/dxvk/rtx_render/rtx_context.h
@@ -39,6 +39,7 @@ namespace dxvk {
class AssetExporter;
class SceneManager;
class TerrainBaker;
+ struct ExternalDrawState;
struct D3D9RtxVertexCaptureData;
@@ -109,6 +110,7 @@ namespace dxvk {
void clearImageView(const Rc& imageView, VkOffset3D offset, VkExtent3D extent, VkImageAspectFlags aspect, VkClearValue value);
void commitGeometryToRT(const DrawParameters& params, DrawCallState& drawCallState);
+ void commitExternalGeometryToRT(ExternalDrawState&& state);
static void blitImageHelper(Rc ctx, const Rc& srcImage, const Rc& dstImage, VkFilter filter);
diff --git a/src/dxvk/rtx_render/rtx_debug_view.cpp b/src/dxvk/rtx_render/rtx_debug_view.cpp
index 9dc7551ee..a6b9c564e 100644
--- a/src/dxvk/rtx_render/rtx_debug_view.cpp
+++ b/src/dxvk/rtx_render/rtx_debug_view.cpp
@@ -66,6 +66,8 @@ namespace dxvk {
{DEBUG_VIEW_POSITION, "Position"},
{DEBUG_VIEW_TEXCOORDS, "Texture Coordinates"},
+ {DEBUG_VIEW_TEXCOORDS_GRADIENT_X, "Texture Coordinates Gradient X"},
+ {DEBUG_VIEW_TEXCOORDS_GRADIENT_Y, "Texture Coordinates Gradient Y"},
{DEBUG_VIEW_TEXCOORD_GENERATION_MODE, "Texture Coordinates Generation Mode"},
{DEBUG_VIEW_VIRTUAL_MOTION_VECTOR, "Virtual Motion Vector"},
{DEBUG_VIEW_SCREEN_SPACE_MOTION_VECTOR, "Screen-Space Motion Vector"},
@@ -82,7 +84,19 @@ namespace dxvk {
{DEBUG_VIEW_MATERIAL_TYPE, "Material Type"},
{DEBUG_VIEW_ALBEDO, "Diffuse Albedo"},
- {DEBUG_VIEW_RAW_ALBEDO, "Diffuse Raw Albedo (RGS only)"},
+ {DEBUG_VIEW_RAW_ALBEDO, "Diffuse Raw Albedo (RGS only)"},
+ {DEBUG_VIEW_OPAQUE_RAW_ALBEDO_RESOLUTION_CHECKERS, "Opaque Material Raw Albedo + Texture Resolution Checkers (RGS only)",
+ "Parameterize via:\n"
+ "Debug Knob [0]: num texels per checker box [Default: 64]\n"
+ "Debug Knob [1]: checkers overlay strength [Default: 0.5]"},
+ {DEBUG_VIEW_OPAQUE_NORMAL_RESOLUTION_CHECKERS, "Opaque Material Normal + Texture Resolution Checkers (RGS only)",
+ "Parameterize via:\n"
+ "Debug Knob [0]: num texels per checker box [Default: 64]\n"
+ "Debug Knob [1]: checkers overlay strength [Default: 0.5]"},
+ {DEBUG_VIEW_OPAQUE_ROUGHNESS_RESOLUTION_CHECKERS, "Opaque Material Roughness + Texture Resolution Checkers (RGS only)",
+ "Parameterize via:\n"
+ "Debug Knob [0]: num texels per checker box [Default: 64]\n"
+ "Debug Knob [1]: checkers overlay strength [Default: 0.5]"},
{DEBUG_VIEW_BASE_REFLECTIVITY, "Base Reflectivity"},
{DEBUG_VIEW_ROUGHNESS, "Isotropic Roughness"},
{DEBUG_VIEW_PERCEPTUAL_ROUGHNESS, "Perceptual Roughness"},
@@ -100,11 +114,11 @@ namespace dxvk {
{DEBUG_VIEW_CASCADE_LEVEL, "Terrain: Cascade Level (RGS only)"},
{DEBUG_VIEW_VIRTUAL_HIT_DISTANCE, "Virtual Hit Distance"},
- {DEBUG_VIEW_PRIMARY_DEPTH, "Primary Depth" },
+ {DEBUG_VIEW_PRIMARY_DEPTH, "Primary Depth"},
{DEBUG_VIEW_SHARED_BIAS_CURRENT_COLOR_MASK, "DLSS Bias Color Mask"},
- {DEBUG_VIEW_IS_INSIDE_FRUSTUM, "Is Inside Frustum" },
+ {DEBUG_VIEW_IS_INSIDE_FRUSTUM, "Is Inside Frustum"},
{DEBUG_VIEW_BLUE_NOISE, "Blue Noise"},
{DEBUG_VIEW_PIXEL_CHECKERBOARD, "Pixel Checkerboard"},
@@ -131,6 +145,8 @@ namespace dxvk {
{DEBUG_VIEW_NEE_CACHE_LIGHT_HISTOGRAM, "NEE Cache Light Histogram"},
{DEBUG_VIEW_NEE_CACHE_HISTOGRAM, "NEE Cache Triangle Histogram"},
+ {DEBUG_VIEW_NEE_CACHE_HASH_MAP, "NEE Cache Hash Map"},
+ {DEBUG_VIEW_NEE_CACHE_ACCUMULATE_MAP, "NEE Cache Accumulate Map"},
{DEBUG_VIEW_NEE_CACHE_SAMPLE_RADIANCE, "NEE Cache Sample Radiance"},
{DEBUG_VIEW_NEE_CACHE_TASK, "NEE Cache Task"},
@@ -208,8 +224,8 @@ namespace dxvk {
{DEBUG_VIEW_NAN, "Inf/NaN Check"},
{DEBUG_SURFACE_LOBE_CONSISTENCY, "Surface/Lobe Consistency Check"},
{DEBUG_VIEW_SCROLLING_LINE, "Scrolling Line"},
- {DEBUG_VIEW_POM_ITERATIONS, "POM Iterations" },
- {DEBUG_VIEW_POM_DIRECT_HIT_POS, "POM Direct Hit Position (Tangent Space)" },
+ {DEBUG_VIEW_POM_ITERATIONS, "POM Iterations"},
+ {DEBUG_VIEW_POM_DIRECT_HIT_POS, "POM Direct Hit Position (Tangent Space)"},
} };
ImGui::ComboWithKey compositeDebugViewCombo = ImGui::ComboWithKey(
@@ -318,23 +334,23 @@ namespace dxvk {
bool filterWords = searchWord.length() > 0;
// Hide unmatched options
- std::vector items;
+ std::vector> items;
items.reserve(debugViewEntries.size());
int itemIndex = -1;
for (int i = 0; i < debugViewEntries.size(); i++) {
- if (debugViewEntries[i].first == lastView) {
+ if (debugViewEntries[i].key == lastView) {
itemIndex = items.size();
}
if (filterWords) {
- std::string name(debugViewEntries[i].second);
+ std::string name(debugViewEntries[i].name);
toLowerCase(name);
- if (debugViewEntries[i].first == lastView || name.find(searchWord) != std::string::npos) {
- items.push_back(debugViewEntries[i].second);
+ if (debugViewEntries[i].key == lastView || name.find(searchWord) != std::string::npos) {
+ items.emplace_back(debugViewEntries[i].name, debugViewEntries[i].tooltip);
}
} else {
- items.push_back(debugViewEntries[i].second);
+ items.emplace_back(debugViewEntries[i].name, debugViewEntries[i].tooltip);
}
}
@@ -348,8 +364,8 @@ namespace dxvk {
ImGui::PopItemWidth();
for (int i = 0; i < debugViewEntries.size(); i++) {
- if (itemIndex != -1 && debugViewEntries[i].second == items[itemIndex]) {
- lastView = debugViewEntries[i].first;
+ if (itemIndex != -1 && debugViewEntries[i].name == items[itemIndex].first) {
+ lastView = debugViewEntries[i].key;
}
}
}
@@ -691,7 +707,7 @@ namespace dxvk {
auto&& debugViewArgs = getCommonDebugViewArgs(ctx.ptr(), rtOutput, common);
Rc cb = getDebugViewConstantsBuffer();
- ctx->updateBuffer(cb, 0, sizeof(DebugViewArgs), &debugViewArgs);
+ ctx->writeToBuffer(cb, 0, sizeof(DebugViewArgs), &debugViewArgs);
ctx->getCommandList()->trackResource(cb);
if (displayType() == DebugViewDisplayType::HDRWaveform) {
diff --git a/src/dxvk/rtx_render/rtx_game_capturer.cpp b/src/dxvk/rtx_render/rtx_game_capturer.cpp
index b625d2415..c68ccb889 100644
--- a/src/dxvk/rtx_render/rtx_game_capturer.cpp
+++ b/src/dxvk/rtx_render/rtx_game_capturer.cpp
@@ -159,10 +159,10 @@ namespace dxvk {
}
void GameCapturer::setInstanceUpdateFlag(const RtInstance& rtInstance, const InstFlag flag) {
- if (isIdle()) {
+ if (!m_state.has()) {
return;
}
- m_cap.instanceFlags[rtInstance.getId()] |= (1 << uint8_t(flag));
+ m_pCap->instanceFlags[rtInstance.getId()] |= (1 << uint8_t(flag));
}
void GameCapturer::trigger(const Rc ctx) {
@@ -185,15 +185,16 @@ namespace dxvk {
assert(!m_state.has());
m_options = getOptions();
-
- m_cap.idStr = hashToString(Capture::nextId++).substr(4, 4);
- m_cap.bCaptureInstances = m_options.bCaptureInstances;
- m_cap.bSkyProbeBaked = false;
- if (m_cap.bCaptureInstances) {
+
+ m_pCap = std::make_unique();
+ m_pCap->idStr = hashToString(Capture::nextId++).substr(4, 4);
+ m_pCap->bCaptureInstances = m_options.bCaptureInstances;
+ m_pCap->bSkyProbeBaked = false;
+ if (m_pCap->bCaptureInstances) {
prepareInstanceStage(ctx);
}
- Logger::info("[GameCapturer][" + m_cap.idStr + "] New capture");
- m_cap.instanceFlags.clear();
+ Logger::info("[GameCapturer][" + m_pCap->idStr + "] New capture");
+ m_pCap->instanceFlags.clear();
m_state.set();
m_state.set();
@@ -202,67 +203,67 @@ namespace dxvk {
void GameCapturer::prepareInstanceStage(const Rc ctx) {
const auto stagePathStr = buildStagePath(m_options.instanceStageName);
- m_cap.instance.stageName = m_options.instanceStageName;
- m_cap.instance.stagePath = stagePathStr;
+ m_pCap->instance.stageName = m_options.instanceStageName;
+ m_pCap->instance.stagePath = stagePathStr;
m_exporter.generateSceneThumbnail(ctx, BASE_DIR + lss::commonDirName::thumbDir, m_options.instanceStageName);
}
void GameCapturer::capture(const Rc ctx, const float dt) {
assert(m_state.has());
- m_cap.currentFrameNum += dt * static_cast(m_options.fps);
+ m_pCap->currentFrameNum += dt * static_cast(m_options.fps);
captureFrame(ctx);
- if (m_cap.numFramesCaptured >= m_options.numFrames) {
+ if (m_pCap->numFramesCaptured >= m_options.numFrames) {
m_state.set();
m_state.set();
}
}
void GameCapturer::captureFrame(const Rc ctx) {
- Logger::debug("[GameCapturer][" + m_cap.idStr + "] Begin frame capture");
- if (m_cap.bCaptureInstances) {
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "] Begin frame capture");
+ if (m_pCap->bCaptureInstances) {
captureCamera();
captureLights();
}
captureInstances(ctx);
- ++m_cap.numFramesCaptured;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "] End frame capture");
+ ++m_pCap->numFramesCaptured;
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "] End frame capture");
}
void GameCapturer::captureCamera() {
- if (isnan(m_cap.camera.fov) ||
- isnan(m_cap.camera.aspectRatio) ||
- isnan(m_cap.camera.nearPlane) ||
- isnan(m_cap.camera.farPlane)) {
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][Camera] New");
+ if (isnan(m_pCap->camera.fov) ||
+ isnan(m_pCap->camera.aspectRatio) ||
+ isnan(m_pCap->camera.nearPlane) ||
+ isnan(m_pCap->camera.farPlane)) {
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][Camera] New");
float shearX, shearY;
const auto projMat = m_sceneManager.getCamera().getViewToProjection();
decomposeProjection(projMat,
- m_cap.camera.aspectRatio,
- m_cap.camera.fov,
- m_cap.camera.nearPlane,
- m_cap.camera.farPlane,
+ m_pCap->camera.aspectRatio,
+ m_pCap->camera.fov,
+ m_pCap->camera.nearPlane,
+ m_pCap->camera.farPlane,
shearX,
shearY,
- m_cap.camera.isLHS,
- m_cap.camera.isReverseZ);
+ m_pCap->camera.isLHS,
+ m_pCap->camera.isReverseZ);
// Infinite projection is legit, but USD doesnt take kindly to it
constexpr float kMaxFarPlane = 100000000;
- if (m_cap.camera.farPlane > kMaxFarPlane) {
- m_cap.camera.farPlane = kMaxFarPlane;
+ if (m_pCap->camera.farPlane > kMaxFarPlane) {
+ m_pCap->camera.farPlane = kMaxFarPlane;
}
// If the app is being rendered upside-down, we need to plan accordingly
- m_cap.camera.bFlipVertAperture = (projMat[0][0] * projMat[1][1] < 0.0f);
- m_cap.camera.firstTime = m_cap.currentFrameNum;
+ m_pCap->camera.bFlipMeshes = (projMat[0][0] * projMat[1][1] < 0.0f);
+ m_pCap->camera.firstTime = m_pCap->currentFrameNum;
}
- assert(!isnan(m_cap.camera.fov));
- assert(!isnan(m_cap.camera.aspectRatio));
- assert(!isnan(m_cap.camera.nearPlane));
- assert(!isnan(m_cap.camera.farPlane));
+ assert(!isnan(m_pCap->camera.fov));
+ assert(!isnan(m_pCap->camera.aspectRatio));
+ assert(!isnan(m_pCap->camera.nearPlane));
+ assert(!isnan(m_pCap->camera.farPlane));
const Matrix4 xform = m_sceneManager.getCamera().getViewToWorld();
- m_cap.camera.finalTime = m_cap.currentFrameNum;
- m_cap.camera.xforms.push_back({ m_cap.currentFrameNum, matrix4ToGfMatrix4d(xform) });
+ m_pCap->camera.finalTime = m_pCap->currentFrameNum;
+ m_pCap->camera.xforms.push_back({ m_pCap->currentFrameNum, matrix4ToGfMatrix4d(xform) });
}
void GameCapturer::captureLights() {
@@ -276,17 +277,17 @@ namespace dxvk {
break;
case RtLightType::Rect:
// Todo: Handle Rect lights
- Logger::err("[GameCapturer][" + m_cap.idStr + "] RectLight not implemented");
+ Logger::err("[GameCapturer][" + m_pCap->idStr + "] RectLight not implemented");
assert(false);
break;
case RtLightType::Disk:
// Todo: Handle Disk lights
- Logger::err("[GameCapturer][" + m_cap.idStr + "] DiskLight not implemented");
+ Logger::err("[GameCapturer][" + m_pCap->idStr + "] DiskLight not implemented");
assert(false);
break;
case RtLightType::Cylinder:
// Todo: Handle Cylinder lights
- Logger::err("[GameCapturer][" + m_cap.idStr + "] CylinderLight not implemented");
+ Logger::err("[GameCapturer][" + m_pCap->idStr + "] CylinderLight not implemented");
assert(false);
break;
case RtLightType::Distant:
@@ -300,9 +301,9 @@ namespace dxvk {
const auto hash = rtLight.getHash();
pxr::GfRotation rotation;
rotation.SetIdentity();
- if (m_cap.sphereLights.count(hash) == 0) {
+ if (m_pCap->sphereLights.count(hash) == 0) {
const std::string name = dxvk::hashToString(hash);
- lss::SphereLight& sphereLight = m_cap.sphereLights[hash];
+ lss::SphereLight& sphereLight = m_pCap->sphereLights[hash];
sphereLight.lightName = name;
const auto colorAndIntensity = rtLight.getColorAndIntensity();
sphereLight.color[0] = colorAndIntensity.r;
@@ -310,8 +311,8 @@ namespace dxvk {
sphereLight.color[2] = colorAndIntensity.b;
sphereLight.intensity = colorAndIntensity.w;
sphereLight.radius = rtLight.getRadius();
- sphereLight.xforms.reserve(m_options.numFrames - m_cap.numFramesCaptured);
- sphereLight.firstTime = m_cap.currentFrameNum;
+ sphereLight.xforms.reserve(m_options.numFrames - m_pCap->numFramesCaptured);
+ sphereLight.firstTime = m_pCap->currentFrameNum;
const dxvk::RtLightShaping& shaping = rtLight.getShaping();
if (shaping.enabled) {
sphereLight.shapingEnabled = true;
@@ -320,21 +321,21 @@ namespace dxvk {
sphereLight.focusExponent = shaping.focusExponent;
rotation = pxr::GfRotation(-pxr::GfVec3d::ZAxis(), pxr::GfVec3f(&shaping.primaryAxis[0]));
}
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][SphereLight:" + name + "] New");
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][SphereLight:" + name + "] New");
}
- lss::SphereLight& sphereLight = m_cap.sphereLights[hash];
+ lss::SphereLight& sphereLight = m_pCap->sphereLights[hash];
const auto position = rtLight.getPosition();
pxr::GfMatrix4d usdXform(rotation, pxr::GfVec3f(&position[0]));
- sphereLight.xforms.push_back({ m_cap.currentFrameNum, usdXform });
- sphereLight.finalTime = m_cap.currentFrameNum;
+ sphereLight.xforms.push_back({ m_pCap->currentFrameNum, usdXform });
+ sphereLight.finalTime = m_pCap->currentFrameNum;
}
void GameCapturer::captureDistantLight(const RtDistantLight& rtLight) {
const auto hash = rtLight.getHash();
- if (m_cap.sphereLights.count(hash) == 0) {
+ if (m_pCap->sphereLights.count(hash) == 0) {
const std::string name = dxvk::hashToString(hash);
- lss::DistantLight& distantLight = m_cap.distantLights[hash];
+ lss::DistantLight& distantLight = m_pCap->distantLights[hash];
distantLight.lightName = name;
const auto colorAndIntensity = rtLight.getColorAndIntensity();
distantLight.color[0] = colorAndIntensity.r;
@@ -343,53 +344,53 @@ namespace dxvk {
distantLight.intensity = colorAndIntensity.w;
distantLight.angle = rtLight.getHalfAngle() * 2.0;
distantLight.direction = pxr::GfVec3f(rtLight.getDirection().data);
- distantLight.firstTime = m_cap.currentFrameNum;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][DistantLight:" + name + "] New");
+ distantLight.firstTime = m_pCap->currentFrameNum;
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][DistantLight:" + name + "] New");
}
- lss::DistantLight& distantLight = m_cap.distantLights[hash];
- distantLight.finalTime = m_cap.currentFrameNum;
+ lss::DistantLight& distantLight = m_pCap->distantLights[hash];
+ distantLight.finalTime = m_pCap->currentFrameNum;
}
void GameCapturer::captureInstances(const Rc ctx) {
- for (const RtInstance* rtInstancePtr : m_sceneManager.getInstanceTable()) {
- assert(rtInstancePtr->getBlas() != nullptr);
+ for (const RtInstance* pRtInstance : m_sceneManager.getInstanceTable()) {
+ assert(pRtInstance->getBlas() != nullptr);
- if (rtInstancePtr->getBlas()->input.cameraType == CameraType::Sky) {
- if (!m_cap.bSkyProbeBaked) {
+ if (pRtInstance->getBlas()->input.cameraType == CameraType::Sky) {
+ if (!m_pCap->bSkyProbeBaked) {
m_exporter.bakeSkyProbe(ctx, BASE_DIR + lss::commonDirName::texDir, commonFileName::bakedSkyProbe);
- m_cap.bSkyProbeBaked = true;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][SkyProbe] Bake scheduled to " +
+ m_pCap->bSkyProbeBaked = true;
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][SkyProbe] Bake scheduled to " +
commonFileName::bakedSkyProbe);
}
}
- const XXH64_hash_t instanceId = rtInstancePtr->getId();
- const uint8_t instanceFlags = m_cap.instanceFlags[instanceId];
- const bool bIsNew = m_cap.instances.count(instanceId) == 0;
+ const XXH64_hash_t instanceId = pRtInstance->getId();
+ const uint8_t instanceFlags = m_pCap->instanceFlags[instanceId];
+ const bool bIsNew = m_pCap->instances.count(instanceId) == 0;
const bool bPointsUpdate = checkInstanceUpdateFlag(instanceFlags, InstFlag::PositionsUpdate);
const bool bNormalsUpdate = checkInstanceUpdateFlag(instanceFlags, InstFlag::NormalsUpdate);
const bool bIndexUpdate = checkInstanceUpdateFlag(instanceFlags, InstFlag::IndexUpdate);
const bool bXformUpdate = checkInstanceUpdateFlag(instanceFlags, InstFlag::XformUpdate);
- Instance& instance = m_cap.instances[instanceId];
+ Instance& instance = m_pCap->instances[instanceId];
if (bIsNew) {
- newInstance(ctx, *rtInstancePtr);
+ newInstance(ctx, *pRtInstance);
}
- if (m_cap.bCaptureInstances && !bIsNew && (bPointsUpdate || bNormalsUpdate || bIndexUpdate)) {
- const BlasEntry* pBlas = rtInstancePtr->getBlas();
+ if (m_pCap->bCaptureInstances && !bIsNew && (bPointsUpdate || bNormalsUpdate || bIndexUpdate)) {
+ const BlasEntry* pBlas = pRtInstance->getBlas();
assert(pBlas != nullptr);
- captureMesh(ctx, instance.meshHash, *pBlas, rtInstancePtr->getCategoryFlags(), false, bPointsUpdate, bNormalsUpdate, bIndexUpdate);
+ captureMesh(ctx, instance.meshHash, *pBlas, pRtInstance->getCategoryFlags(), false, bPointsUpdate, bNormalsUpdate, bIndexUpdate);
}
- if (m_cap.bCaptureInstances && (bIsNew || bXformUpdate)) {
- instance.lssData.xforms.push_back({ m_cap.currentFrameNum, matrix4ToGfMatrix4d(rtInstancePtr->getTransform()) });
- const SkinningData& skinData = rtInstancePtr->getBlas()->input.getSkinningState();
+ if (m_pCap->bCaptureInstances && (bIsNew || bXformUpdate)) {
+ instance.lssData.xforms.push_back({ m_pCap->currentFrameNum, matrix4ToGfMatrix4d(pRtInstance->getTransform()) });
+ const SkinningData& skinData = pRtInstance->getBlas()->input.getSkinningState();
if (skinData.numBones > 0) {
- instance.lssData.boneXForms.push_back({ m_cap.currentFrameNum, matrix4VecToGfMatrix4dVec(skinData.pBoneMatrices) });
+ instance.lssData.boneXForms.push_back({ m_pCap->currentFrameNum, matrix4VecToGfMatrix4dVec(skinData.pBoneMatrices) });
}
}
- instance.lssData.finalTime = m_cap.currentFrameNum;
- instance.lssData.isSky = (rtInstancePtr->getBlas()->input.cameraType == CameraType::Sky);
- instance.lssData.metadata = createDrawCallMetadata(*rtInstancePtr);
+ instance.lssData.finalTime = m_pCap->currentFrameNum;
+ instance.lssData.isSky = (pRtInstance->getBlas()->input.cameraType == CameraType::Sky);
+ instance.lssData.metadata = createDrawCallMetadata(*pRtInstance);
}
}
@@ -402,7 +403,7 @@ namespace dxvk {
const LegacyMaterialData& material = pBlas->getMaterialData(matHash);
- const bool bIsNewMat = (matHash != 0x0) && (m_cap.materials.count(matHash) == 0);
+ const bool bIsNewMat = (matHash != 0x0) && (m_pCap->materials.count(matHash) == 0);
if (bIsNewMat) {
captureMaterial(ctx, material, !rtInstance.surface.alphaState.isFullyOpaque);
}
@@ -411,48 +412,53 @@ namespace dxvk {
size_t instanceNum = 0;
{
std::lock_guard lock(m_meshMutex);
- bIsNewMesh = m_cap.meshes.count(meshHash) == 0;
+ bIsNewMesh = m_pCap->meshes.count(meshHash) == 0;
if (bIsNewMesh) {
- m_cap.meshes[meshHash] = std::make_shared();
- m_cap.meshes[meshHash]->instanceCount = 0;
- m_cap.meshes[meshHash]->matHash = matHash;
+ m_pCap->meshes[meshHash] = std::make_shared();
+ m_pCap->meshes[meshHash]->instanceCount = 0;
+ m_pCap->meshes[meshHash]->matHash = matHash;
}
- instanceNum = m_cap.meshes[meshHash]->instanceCount++;
+ instanceNum = m_pCap->meshes[meshHash]->instanceCount++;
}
if (bIsNewMesh) {
captureMesh(ctx, meshHash, *pBlas, rtInstance.getCategoryFlags(), true, true, true, true);
}
const XXH64_hash_t instanceId = rtInstance.getId();
- Instance& instance = m_cap.instances[instanceId];
+ Instance& instance = m_pCap->instances[instanceId];
instance.meshHash = meshHash;
instance.matHash = matHash;
instance.meshInstNum = instanceNum;
- instance.lssData.firstTime = m_cap.currentFrameNum;
- instance.lssData.xforms.reserve(m_options.numFrames - m_cap.numFramesCaptured);
- instance.lssData.metadata = createDrawCallMetadata(rtInstance);
+ instance.lssData.firstTime = m_pCap->currentFrameNum;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][Inst:" + hashToString(instanceId) + "] New");
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][Inst:" + hashToString(instanceId) + "] New");
}
void GameCapturer::captureMaterial(const Rc ctx, const LegacyMaterialData& materialData, const bool bEnableOpacity) {
- const std::string matName = dxvk::hashToString(materialData.getHash());
+ lss::Material lssMat; // to be populated
- //Export Textures
+ // Resolve material name
+ const std::string matName = dxvk::hashToString(materialData.getHash());
+ lssMat.matName = matName;
+ // Export Textures
const std::string albedoTexFilename(matName + lss::ext::dds);
m_exporter.dumpImageToFile(ctx, BASE_DIR + lss::commonDirName::texDir,
albedoTexFilename,
materialData.getColorTexture().getImageView()->image());
-
const std::string albedoTexPath = str::format(BASE_DIR + lss::commonDirName::texDir, albedoTexFilename);
-
- // Export Material
- lss::Material lssMat;
- lssMat.matName = matName;
lssMat.albedoTexPath = albedoTexPath;
+ // Opacity
lssMat.enableOpacity = bEnableOpacity;
- m_cap.materials[materialData.getHash()].lssData = lssMat;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][Mat:" + matName + "] New");
+ // Collect sampler info
+ const auto& samplerCreateInfo = materialData.getSampler()->info();
+ lssMat.sampler.addrModeU = samplerCreateInfo.addressModeU;
+ lssMat.sampler.addrModeV = samplerCreateInfo.addressModeV;
+ lssMat.sampler.filter = samplerCreateInfo.magFilter;
+ lssMat.sampler.borderColor = samplerCreateInfo.borderColor;
+
+ // Set populated LSS Material in our cache
+ m_pCap->materials[materialData.getHash()].lssData = lssMat;
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][Mat:" + matName + "] New");
}
void GameCapturer::captureMesh(const Rc ctx,
@@ -472,7 +478,7 @@ namespace dxvk {
std::shared_ptr pMesh;
{
std::lock_guard lock(m_meshMutex);
- pMesh = m_cap.meshes[currentMeshHash];
+ pMesh = m_pCap->meshes[currentMeshHash];
}
// Note: Ensures that reading a Vec3 from the position buffer will result in the proper values. This can be extended if
@@ -504,39 +510,39 @@ namespace dxvk {
pMesh->lssData.isDoubleSided = isDoubleSided;
pMesh->lssData.numBones = skinData.numBones;
pMesh->lssData.bonesPerVertex = skinData.numBonesPerVertex;
- Logger::debug("[GameCapturer][" + m_cap.idStr + "][Mesh:" + pMesh->lssData.meshName + "] New");
+ Logger::debug("[GameCapturer][" + m_pCap->idStr + "][Mesh:" + pMesh->lssData.meshName + "] New");
}
if (bCapturePositions && geomData.positionBuffer.defined()) {
if (skinData.numBones > 0) {
- captureMeshPositions(ctx, rasterGeomData.vertexCount, rasterGeomData.positionBuffer, m_cap.currentFrameNum, pMesh);
+ captureMeshPositions(ctx, rasterGeomData.vertexCount, rasterGeomData.positionBuffer, m_pCap->currentFrameNum, pMesh);
} else {
- captureMeshPositions(ctx, geomData.vertexCount, geomData.positionBuffer, m_cap.currentFrameNum, pMesh);
+ captureMeshPositions(ctx, geomData.vertexCount, geomData.positionBuffer, m_pCap->currentFrameNum, pMesh);
}
}
if (bCaptureNormals && geomData.normalBuffer.defined()) {
if (skinData.numBones > 0) {
- captureMeshNormals(ctx, rasterGeomData.vertexCount, rasterGeomData.normalBuffer, m_cap.currentFrameNum, pMesh);
+ captureMeshNormals(ctx, rasterGeomData.vertexCount, rasterGeomData.normalBuffer, m_pCap->currentFrameNum, pMesh);
} else {
- captureMeshNormals(ctx, geomData.vertexCount, geomData.normalBuffer, m_cap.currentFrameNum, pMesh);
+ captureMeshNormals(ctx, geomData.vertexCount, geomData.normalBuffer, m_pCap->currentFrameNum, pMesh);
}
}
if (bCaptureIndices && geomData.indexBuffer.defined()) {
- captureMeshIndices(ctx, geomData, m_cap.currentFrameNum, pMesh);
+ captureMeshIndices(ctx, geomData, m_pCap->currentFrameNum, pMesh);
}
if (bIsNewMesh && geomData.texcoordBuffer.defined()) {
- captureMeshTexCoords(ctx, geomData, m_cap.currentFrameNum, pMesh);
+ captureMeshTexCoords(ctx, geomData, m_pCap->currentFrameNum, pMesh);
}
if (bIsNewMesh && geomData.color0Buffer.defined()) {
- captureMeshColor(ctx, geomData, m_cap.currentFrameNum, pMesh);
+ captureMeshColor(ctx, geomData, m_pCap->currentFrameNum, pMesh);
}
if (bIsNewMesh && skinData.numBones > 0) {
- captureMeshBlending(ctx, rasterGeomData, m_cap.currentFrameNum, pMesh);
+ captureMeshBlending(ctx, rasterGeomData, m_pCap->currentFrameNum, pMesh);
pMesh->lssData.boneXForms = matrix4VecToGfMatrix4dVec(skinData.pBoneMatrices);
}
}
@@ -548,7 +554,7 @@ namespace dxvk {
const float currentFrameNum,
std::shared_ptr pMesh) {
- AssetExporter::BufferCallback captureMeshPositionsAsync = [ctx, numVertices, inputPositionBuffer, currentFrameNum, pMesh](Rc posBuf) {
+ AssetExporter::BufferCallback captureMeshPositionsAsync = [this, ctx, numVertices, inputPositionBuffer, currentFrameNum, pMesh](Rc posBuf) {
// Prep helper vars
constexpr size_t positionSubElementSize = sizeof(float);
const size_t positionStride = inputPositionBuffer.stride() / positionSubElementSize;
@@ -562,8 +568,16 @@ namespace dxvk {
// Copy GPU buffer to local VtArray
pxr::VtArray positions;
positions.reserve(numVertices);
+ OriginCalc originCalc;
for (size_t idx = 0; idx < numVertices; ++idx) {
- positions.push_back(pxr::GfVec3f(&pVkPosBuf[idx * positionStride]));
+ const pxr::GfVec3f& pos = *reinterpret_cast(&pVkPosBuf[idx * positionStride]);
+ if(m_correctBakedTransforms) {
+ originCalc.compareAndSwap(pos);
+ }
+ positions.push_back(pos);
+ }
+ if(m_correctBakedTransforms) {
+ pMesh->originCalc.compareAndSwap(originCalc);
}
assert(positions.size() > 0);
// Create comparison function that returns float
@@ -866,11 +880,12 @@ namespace dxvk {
assert(!m_state.has());
assert(!m_state.has());
static auto exportThreadTask = [this](const Rc ctx,
- Capture cap,
+ std::unique_ptr pCap,
State* pState,
CompletedCapture* complete,
const float framesPerSecond,
const bool bUseLssUsdPlugins) {
+ Capture& cap = *pCap;
m_exporter.waitForAllExportsToComplete();
assert(pState->has());
const auto exportPrep = prepExport(cap, framesPerSecond, bUseLssUsdPlugins);
@@ -897,17 +912,16 @@ namespace dxvk {
m_state.set();
std::thread(exportThreadTask,
ctx,
- std::move(m_cap),
+ std::move(m_pCap),
&m_state,
&m_completeCapture,
static_cast(m_options.fps),
m_bUseLssUsdPlugins).detach();
- m_cap = Capture(); // reset to default
}
lss::Export GameCapturer::prepExport(const Capture& cap,
- const float framesPerSecond,
- const bool bUseLssUsdPlugins) {
+ const float framesPerSecond,
+ const bool bUseLssUsdPlugins) {
lss::Export exportPrep;
prepExportMetaData(cap, framesPerSecond, bUseLssUsdPlugins, exportPrep);
prepExportMaterials(cap, exportPrep);
@@ -944,6 +958,7 @@ namespace dxvk {
exportPrep.meta.renderingSettingsDict[pair.first] = pair.second->genericValueToString(RtxOptionImpl::ValueType::Value);
}
}
+ exportPrep.meta.bCorrectBakedTransforms = m_correctBakedTransforms;
exportPrep.debugId = cap.idStr;
exportPrep.baseExportPath = BASE_DIR;
@@ -959,38 +974,45 @@ namespace dxvk {
}
}
- void GameCapturer::prepExportMeshes(const Capture& cap,
- lss::Export& exportPrep) {
+ void GameCapturer::prepExportMeshes(const Capture& cap, lss::Export& exportPrep) {
+ OriginCalc stageOriginCalc;
for (auto& [hash, pMesh] : cap.meshes) {
std::unique_lock lock(pMesh->meshSync.mutex);
pMesh->meshSync.cond.wait(lock,
[pNumOutstanding = &pMesh->meshSync.numOutstanding] { return *pNumOutstanding == 0; });
- auto& exportMesh = exportPrep.meshes[hash];
if (pMesh->lssData.numIndices == 0 && pMesh->lssData.numVertices == 0) {
continue;
}
if (cap.materials.count(pMesh->matHash) > 0) {
pMesh->lssData.matId = pMesh->matHash;
}
- exportMesh = pMesh->lssData;
+ if(m_correctBakedTransforms) {
+ pMesh->lssData.origin = pMesh->originCalc.calc();
+ stageOriginCalc.compareAndSwap(pMesh->lssData.origin);
+ }
+ exportPrep.meshes[hash] = pMesh->lssData;
+ }
+ if(m_correctBakedTransforms) {
+ exportPrep.stageOrigin = stageOriginCalc.calc();
}
}
- void GameCapturer::prepExportInstances(const Capture& cap,
- lss::Export& exportPrep) {
+ void GameCapturer::prepExportInstances(const Capture& cap, lss::Export& exportPrep) {
for (auto& [hash, instance] : cap.instances) {
if (instance.meshHash == 0) {
continue;
}
- auto& exportInstance =
- exportPrep.instances.emplace(hash, instance.lssData).first->second;
+ auto& exportInstance = exportPrep.instances[hash];
+ exportInstance = instance.lssData;
+
assert(cap.meshes.count(instance.meshHash) > 0);
+ exportInstance.meshId = instance.meshHash;
+
const auto pMesh = cap.meshes.at(instance.meshHash);
- exportInstance.instanceName = pMesh->lssData.meshName + "_" + std::to_string(instance.meshInstNum);
if (cap.materials.count(pMesh->matHash) > 0) {
exportInstance.matId = instance.matHash;
}
- exportInstance.meshId = instance.meshHash;
+ exportInstance.instanceName = pMesh->lssData.meshName + "_" + std::to_string(instance.meshInstNum);
}
}
@@ -1015,5 +1037,4 @@ namespace dxvk {
assert(pFlattenedStage);
Logger::info("[GameCapturer][" + exportPrep.debugId + "] USD capture flattened.");
}
-
}
diff --git a/src/dxvk/rtx_render/rtx_game_capturer.h b/src/dxvk/rtx_render/rtx_game_capturer.h
index 5a6c980f7..906f56393 100644
--- a/src/dxvk/rtx_render/rtx_game_capturer.h
+++ b/src/dxvk/rtx_render/rtx_game_capturer.h
@@ -21,6 +21,7 @@
*/
#pragma once
+#include "rtx_game_capturer_utils.h"
#include "rtx_options.h"
#include "../../lssusd/game_exporter_types.h"
@@ -57,6 +58,13 @@ struct LegacyMaterialData;
class GameCapturer : public RcObject
{
public:
+ RW_RTX_OPTION("rtx.capture", bool, correctBakedTransforms, false,
+ "Some games bake world transforms into mesh vertices. If individually captured\n"
+ "meshes appear to be way off in the middle of nowhere OR instanced meshes appear\n"
+ "to all have identity xform matrices, enabling will attempt to correct this and\n"
+ "improve stage + mesh viewability in tools.\n"
+ "Hashes are unaffected.");
+
GameCapturer(DxvkDevice* const pDevice, SceneManager& sceneManager, AssetExporter& exporter);
~GameCapturer();
@@ -125,10 +133,11 @@ class GameCapturer : public RcObject
};
struct Mesh {
- lss::Mesh lssData;
- size_t instanceCount = 0;
- XXH64_hash_t matHash;
- MeshSync meshSync;
+ lss::Mesh lssData;
+ size_t instanceCount = 0;
+ XXH64_hash_t matHash;
+ MeshSync meshSync;
+ AtomicOriginCalc originCalc;
};
struct Instance {
@@ -280,7 +289,8 @@ class GameCapturer : public RcObject
std::unordered_map materials;
std::unordered_map instances;
std::unordered_map instanceFlags;
- } m_cap;
+ };
+ std::unique_ptr m_pCap;
};
}
\ No newline at end of file
diff --git a/src/dxvk/rtx_render/rtx_game_capturer_utils.h b/src/dxvk/rtx_render/rtx_game_capturer_utils.h
new file mode 100644
index 000000000..b1afb5966
--- /dev/null
+++ b/src/dxvk/rtx_render/rtx_game_capturer_utils.h
@@ -0,0 +1,110 @@
+/*
+* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*/
+#pragma once
+
+#include "../../lssusd/usd_include_begin.h"
+#include
+#include "../../lssusd/usd_include_end.h"
+
+#include
+
+namespace dxvk {
+
+struct AtomicOriginCalc; // Forward declare
+struct OriginCalc {
+ inline void compareAndSwap(const pxr::GfVec3f& vec3) {
+ replaceMin(&vec3[0]);
+ replaceMax(&vec3[0]);
+ }
+ inline void compareAndSwap(const OriginCalc& other) {
+ replaceMin(other.min);
+ replaceMax(other.max);
+ }
+ inline pxr::GfVec3f calc() const {
+ return (pxr::GfVec3f(&min[0]) + pxr::GfVec3f(&max[0])) / 2;
+ }
+private:
+ static constexpr float fMax = std::numeric_limits::max();
+ float min[3] = { fMax, fMax, fMax};
+ float max[3] = {-fMax,-fMax,-fMax};
+ inline void replaceMin(const float* const vec3) {
+ min[0] = std::min(min[0],vec3[0]);
+ min[1] = std::min(min[1],vec3[1]);
+ min[2] = std::min(min[2],vec3[2]);
+ }
+ inline void replaceMax(const float* const vec3) {
+ max[0] = std::max(max[0],vec3[0]);
+ max[1] = std::max(max[1],vec3[1]);
+ max[2] = std::max(max[2],vec3[2]);
+ }
+ friend AtomicOriginCalc;
+};
+
+struct AtomicOriginCalc {
+ inline void compareAndSwap(const pxr::GfVec3f& vec3) {
+ replaceMin(&vec3[0]);
+ replaceMax(&vec3[0]);
+ }
+ inline void compareAndSwap(const OriginCalc& other) {
+ replaceMin(other.min);
+ replaceMax(other.max);
+ }
+ inline pxr::GfVec3f calc() const {
+ return (pxr::GfVec3f{min[0].load(),min[1].load(),min[2].load()} +
+ pxr::GfVec3f{max[0].load(),max[1].load(),max[2].load()})
+ / 2;
+ }
+ inline void reset() {
+ for(size_t i = 0; i < 3; ++i) {
+ min[i].store(fMax);
+ max[i].store(-fMax);
+ }
+ }
+private:
+ static constexpr float fMax = std::numeric_limits::max();
+ std::atomic min[3] = { fMax, fMax, fMax};
+ std::atomic max[3] = {-fMax,-fMax,-fMax};
+ inline void replaceMin(const float* const vec3) {
+ swapIfLess(min[0],vec3[0]);
+ swapIfLess(min[1],vec3[1]);
+ swapIfLess(min[2],vec3[2]);
+ }
+ inline void replaceMax(const float* const vec3) {
+ swapIfGreater(max[0],vec3[0]);
+ swapIfGreater(max[1],vec3[1]);
+ swapIfGreater(max[2],vec3[2]);
+ }
+ static inline void swapIfLess(std::atomic& atomic, const float other) {
+ float val = fMax;
+ do {
+ val = atomic.load();
+ } while(val > other && !atomic.compare_exchange_weak(val, other));
+ }
+ static void swapIfGreater(std::atomic& atomic, const float other) {
+ float val = -fMax;
+ do {
+ val = atomic.load();
+ } while(val < other && !atomic.compare_exchange_weak(val, other));
+ }
+};
+
+}
diff --git a/src/dxvk/rtx_render/rtx_geometry_utils.cpp b/src/dxvk/rtx_render/rtx_geometry_utils.cpp
index 9b3a4ec91..a00c3b1ce 100644
--- a/src/dxvk/rtx_render/rtx_geometry_utils.cpp
+++ b/src/dxvk/rtx_render/rtx_geometry_utils.cpp
@@ -245,8 +245,8 @@ namespace dxvk {
for (uint32_t idx = 0; idx < params.numVertices; idx++) {
skinning(idx, &dstPosition[0], &dstNormal[0], srcPosition, srcBlendWeight, srcBlendIndices, srcNormal, params);
- ctx->updateBuffer(geo.positionBuffer.buffer(), geo.positionBuffer.offsetFromSlice() + idx * geo.positionBuffer.stride(), sizeof(dstPosition), &dstPosition[0], true);
- ctx->updateBuffer(geo.normalBuffer.buffer(), geo.normalBuffer.offsetFromSlice() + idx * geo.normalBuffer.stride(), sizeof(dstNormal), &dstNormal[0], true);
+ ctx->writeToBuffer(geo.positionBuffer.buffer(), geo.positionBuffer.offsetFromSlice() + idx * geo.positionBuffer.stride(), sizeof(dstPosition), &dstPosition[0], true);
+ ctx->writeToBuffer(geo.normalBuffer.buffer(), geo.normalBuffer.offsetFromSlice() + idx * geo.normalBuffer.stride(), sizeof(dstNormal), &dstNormal[0], true);
}
}
}
@@ -560,7 +560,7 @@ namespace dxvk {
generateIndices(idx, dst, src, cb);
}
- ctx->updateBuffer(dstSlice.buffer(), 0, cb.primCount * 3 * sizeof(uint16_t), dst, true);
+ ctx->writeToBuffer(dstSlice.buffer(), 0, cb.primCount * 3 * sizeof(uint16_t), dst, true);
}
}
@@ -733,7 +733,7 @@ namespace dxvk {
interleaver::interleave(i, dst, inputData.positionData, inputData.normalData, inputData.texcoordData, inputData.vertexColorData, args);
}
- ctx->updateBuffer(output.buffer, 0, input.vertexCount * output.stride, dst, true);
+ ctx->writeToBuffer(output.buffer, 0, input.vertexCount * output.stride, dst, true);
}
uint32_t offset = 0;
diff --git a/src/dxvk/rtx_render/rtx_imgui.cpp b/src/dxvk/rtx_render/rtx_imgui.cpp
index 8968f68e4..9387c14a5 100644
--- a/src/dxvk/rtx_render/rtx_imgui.cpp
+++ b/src/dxvk/rtx_render/rtx_imgui.cpp
@@ -24,4 +24,137 @@ namespace ImGui {
bool Checkbox(const char* label, dxvk::RtxOption* rtxOption) {
return IMGUI_ADD_TOOLTIP(Checkbox(label, &rtxOption->getValue()), rtxOption->getDescription());
}
+
+ static bool Items_PairGetter(void* data, int idx, const char** out_text, const char** out_tooltip) {
+ std::pair* items = reinterpret_cast*>(data);
+ if (out_text) {
+ *out_text = items[idx].first;
+ }
+ if (out_tooltip) {
+ *out_tooltip = items[idx].second;
+ }
+ return true;
+ }
+
+ // Copied from imgui_widgets.cpp
+ static float CalcMaxPopupHeightFromItemCount(int items_count) {
+ ImGuiContext& g = *GImGui;
+ if (items_count <= 0)
+ return FLT_MAX;
+ return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
+ }
+
+ bool ImGui::Combo(const char* label, int* current_item, const std::pair items[], int items_count, int height_in_items) {
+ const bool value_changed = Combo(label, current_item, Items_PairGetter, (void*) items, items_count, height_in_items);
+ return value_changed;
+ }
+
+ // Old API, prefer using BeginCombo() nowadays if you can.
+ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**, const char**), void* data, int items_count, int popup_max_height_in_items) {
+ ImGuiContext& g = *GImGui;
+
+ // Call the getter to obtain the preview string which is a parameter to BeginCombo()
+ const char* preview_value = NULL;
+ if (*current_item >= 0 && *current_item < items_count) {
+ items_getter(data, *current_item, &preview_value, nullptr);
+ }
+
+ // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
+ if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) {
+ SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
+ }
+
+ if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) {
+ return false;
+ }
+
+ // Display items
+ // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
+ bool value_changed = false;
+ for (int i = 0; i < items_count; i++) {
+ PushID(i);
+ const bool item_selected = (i == *current_item);
+ const char* item_text;
+ const char* item_tooltip;
+ if (!items_getter(data, i, &item_text, &item_tooltip)) {
+ item_text = "*Unknown item*";
+ }
+ if (Selectable(item_text, item_selected)) {
+ value_changed = true;
+ *current_item = i;
+ }
+ if (item_selected) {
+ SetItemDefaultFocus();
+ }
+ if (item_tooltip && item_tooltip[0] != '\0' && ImGui::IsItemHovered()) {
+ SetTooltipUnformatted(item_tooltip);
+ }
+ PopID();
+ }
+
+ EndCombo();
+
+ if (value_changed) {
+ MarkItemEdited(g.LastItemData.ID);
+ }
+
+ return value_changed;
+ }
+
+ bool ImGui::ListBox(const char* label, int* current_item, const std::pair items[], int items_count, int height_items) {
+ const bool value_changed = ListBox(label, current_item, Items_PairGetter, (void*) items, items_count, height_items);
+ return value_changed;
+ }
+
+ // This is merely a helper around BeginListBox(), EndListBox().
+ // Considering using those directly to submit custom data or store selection differently.
+ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**, const char**), void* data, int items_count, int height_in_items) {
+ ImGuiContext& g = *GImGui;
+
+ // Calculate size from "height_in_items"
+ if (height_in_items < 0) {
+ height_in_items = ImMin(items_count, 7);
+ }
+ float height_in_items_f = height_in_items + 0.25f;
+ ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
+
+ if (!BeginListBox(label, size)) {
+ return false;
+ }
+
+ // Assume all items have even height (= 1 line of text). If you need items of different height,
+ // you can create a custom version of ListBox() in your code without using the clipper.
+ bool value_changed = false;
+ ImGuiListClipper clipper;
+ clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
+ while (clipper.Step())
+ for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
+ const char* item_text;
+ const char* item_tooltip;
+ if (!items_getter(data, i, &item_text, &item_tooltip)) {
+ item_text = "*Unknown item*";
+ }
+
+ PushID(i);
+ const bool item_selected = (i == *current_item);
+ if (Selectable(item_text, item_selected)) {
+ *current_item = i;
+ value_changed = true;
+ }
+ if (item_selected) {
+ SetItemDefaultFocus();
+ }
+ if (item_tooltip && item_tooltip[0] != '\0' && ImGui::IsItemHovered()) {
+ SetTooltipUnformatted(item_tooltip);
+ }
+ PopID();
+ }
+ EndListBox();
+
+ if (value_changed) {
+ MarkItemEdited(g.LastItemData.ID);
+ }
+
+ return value_changed;
+ }
}
diff --git a/src/dxvk/rtx_render/rtx_imgui.h b/src/dxvk/rtx_render/rtx_imgui.h
index 93d1f63a2..b5a34b7e4 100644
--- a/src/dxvk/rtx_render/rtx_imgui.h
+++ b/src/dxvk/rtx_render/rtx_imgui.h
@@ -41,6 +41,11 @@ namespace ImGui {
#define IMGUI_ADD_TOOLTIP(imguiCommand, tooltip) ImGui::addTooltipAndPassthroughValue((imguiCommand), tooltip)
IMGUI_API bool Checkbox(const char* label, dxvk::RtxOption* rtxOption);
+ IMGUI_API bool Combo(const char* label, int* current_item, const std::pair items[], int items_count, int popup_max_height_in_items = -1);
+ IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text, const char** out_tooltip), void* data, int items_count, int popup_max_height_in_items = -1);
+ IMGUI_API bool ListBox(const char* label, int* current_item, const std::pair items[], int items_count, int height_in_items = -1);
+ IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text, const char** out_tooltip), void* data, int items_count, int height_in_items = -1);
+
// Variant handling RtxOption as input
template
@@ -225,19 +230,24 @@ namespace ImGui {
IMGUI_API bool SliderFloat4(const char* label, dxvk::RtxOption* rtxOption, Args&& ... args) {
return IMGUI_ADD_TOOLTIP(SliderFloat4(label, rtxOption->getValue().data, std::forward(args)...), rtxOption->getDescription());
}
-
+
// Combo Box with unique key per combo entry
// The combo entries are displayed in the order they appear in ComboEntries
template
class ComboWithKey {
public:
- using ComboEntries = std::vector>;
+ struct ComboEntry {
+ T key;
+ const char* name = nullptr;
+ const char* tooltip = nullptr;
+ };
+ using ComboEntries = std::vector;
ComboWithKey(const char* widgetName, ComboEntries&& comboEntries)
: m_comboEntries { std::move(comboEntries) }
, m_widgetName { widgetName } {
for (int i = 0; i < m_comboEntries.size(); i++) {
- T key = m_comboEntries[i].first;
+ T key = m_comboEntries[i].key;
assert(m_keyToComboIdx.find(key) == m_keyToComboIdx.end() && "Duplicate key found");
m_keyToComboIdx[key] = i;
}
@@ -258,7 +268,7 @@ namespace ImGui {
bool isChanged = Combo(m_widgetName, &comboIdx, getString, static_cast(&m_comboEntries), static_cast(m_comboEntries.size()));
- *key = m_comboEntries[comboIdx].first;
+ *key = m_comboEntries[comboIdx].key;
return isChanged;
}
@@ -270,13 +280,18 @@ namespace ImGui {
}
private:
- static bool getString(void* data, int entryIdx, const char** out_text) {
+ static bool getString(void* data, int entryIdx, const char** out_text, const char** out_tooltip) {
const ComboEntries& v = *reinterpret_cast