diff --git a/src/common/rt64_user_configuration.cpp b/src/common/rt64_user_configuration.cpp index 376653b..c8838ba 100644 --- a/src/common/rt64_user_configuration.cpp +++ b/src/common/rt64_user_configuration.cpp @@ -18,7 +18,6 @@ namespace RT64 { j["resolution"] = cfg.resolution; j["displayBuffering"] = cfg.displayBuffering; j["antialiasing"] = cfg.antialiasing; - j["hardwareResolve"] = cfg.hardwareResolve; j["resolutionMultiplier"] = cfg.resolutionMultiplier; j["downsampleMultiplier"] = cfg.downsampleMultiplier; j["filtering"] = cfg.filtering; @@ -31,6 +30,7 @@ namespace RT64 { j["refreshRate"] = cfg.refreshRate; j["refreshRateTarget"] = cfg.refreshRateTarget; j["internalColorFormat"] = cfg.internalColorFormat; + j["hardwareResolve"] = cfg.hardwareResolve; j["idleWorkActive"] = cfg.idleWorkActive; j["developerMode"] = cfg.developerMode; } @@ -41,7 +41,6 @@ namespace RT64 { cfg.resolution = j.value("resolution", defaultCfg.resolution); cfg.displayBuffering = j.value("displayBuffering", defaultCfg.displayBuffering); cfg.antialiasing = j.value("antialiasing", defaultCfg.antialiasing); - cfg.hardwareResolve = j.value("hardwareResolve", defaultCfg.hardwareResolve); cfg.resolutionMultiplier = j.value("resolutionMultiplier", defaultCfg.resolutionMultiplier); cfg.downsampleMultiplier = j.value("downsampleMultiplier", defaultCfg.downsampleMultiplier); cfg.filtering = j.value("filtering", defaultCfg.filtering); @@ -54,6 +53,7 @@ namespace RT64 { cfg.refreshRate = j.value("refreshRate", defaultCfg.refreshRate); cfg.refreshRateTarget = j.value("refreshRateTarget", defaultCfg.refreshRateTarget); cfg.internalColorFormat = j.value("internalColorFormat", defaultCfg.internalColorFormat); + cfg.hardwareResolve = j.value("hardwareResolve", defaultCfg.hardwareResolve); cfg.idleWorkActive = j.value("idleWorkActive", defaultCfg.idleWorkActive); cfg.developerMode = j.value("developerMode", defaultCfg.developerMode); } @@ -72,7 +72,6 @@ namespace RT64 { resolution = Resolution::WindowIntegerScale; displayBuffering = DisplayBuffering::Double; antialiasing = Antialiasing::None; - hardwareResolve = true; resolutionMultiplier = 2.0f; downsampleMultiplier = 1; filtering = Filtering::AntiAliasedPixelScaling; @@ -85,6 +84,7 @@ namespace RT64 { refreshRate = RefreshRate::Original; refreshRateTarget = 60; internalColorFormat = InternalColorFormat::Automatic; + hardwareResolve = HardwareResolve::Automatic; idleWorkActive = true; developerMode = false; } @@ -100,6 +100,7 @@ namespace RT64 { clampEnum(upscale2D); clampEnum(refreshRate); clampEnum(internalColorFormat); + clampEnum(hardwareResolve); resolutionMultiplier = std::clamp(resolutionMultiplier, 0.0f, ResolutionMultiplierLimit); downsampleMultiplier = std::clamp(downsampleMultiplier, 1, ResolutionMultiplierLimit); aspectTarget = std::clamp(aspectTarget, 0.1f, 100.0f); diff --git a/src/common/rt64_user_configuration.h b/src/common/rt64_user_configuration.h index 574a984..0ade912 100644 --- a/src/common/rt64_user_configuration.h +++ b/src/common/rt64_user_configuration.h @@ -76,11 +76,17 @@ namespace RT64 { OptionCount }; + enum class HardwareResolve { + Disabled, + Enabled, + Automatic, + OptionCount + }; + GraphicsAPI graphicsAPI; Resolution resolution; DisplayBuffering displayBuffering; Antialiasing antialiasing; - bool hardwareResolve; double resolutionMultiplier; int downsampleMultiplier; Filtering filtering; @@ -93,6 +99,7 @@ namespace RT64 { RefreshRate refreshRate; int refreshRateTarget; InternalColorFormat internalColorFormat; + HardwareResolve hardwareResolve; bool idleWorkActive; bool developerMode; @@ -153,6 +160,12 @@ namespace RT64 { { UserConfiguration::InternalColorFormat::Automatic, "Automatic" } }); + NLOHMANN_JSON_SERIALIZE_ENUM(UserConfiguration::HardwareResolve, { + { UserConfiguration::HardwareResolve::Disabled, "Disabled" }, + { UserConfiguration::HardwareResolve::Enabled, "Enabled" }, + { UserConfiguration::HardwareResolve::Automatic, "Automatic" } + }); + struct ConfigurationJSON { static bool read(UserConfiguration &cfg, std::istream &stream); static bool write(const UserConfiguration &cfg, std::ostream &stream); diff --git a/src/d3d12/rt64_d3d12.cpp b/src/d3d12/rt64_d3d12.cpp index 7ba9331..2c4de2c 100644 --- a/src/d3d12/rt64_d3d12.cpp +++ b/src/d3d12/rt64_d3d12.cpp @@ -3232,6 +3232,13 @@ namespace RT64 { capabilities.sampleLocations = samplePositionsOption; description.name = win32::Utf16ToUtf8(adapterDesc.Description); description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory; + description.vendor = RenderDeviceVendor(adapterDesc.VendorId); + + LARGE_INTEGER adapterVersion = {}; + res = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &adapterVersion); + if (SUCCEEDED(res)) { + description.driverVersion = adapterVersion.QuadPart; + } if (preferUserChoice) { break; diff --git a/src/hle/rt64_application.cpp b/src/hle/rt64_application.cpp index 8d7e84a..3ce6c33 100644 --- a/src/hle/rt64_application.cpp +++ b/src/hle/rt64_application.cpp @@ -156,29 +156,63 @@ namespace RT64 { return SetupResult::GraphicsDeviceNotFound; } + // Detect if the application should use hardware resolve or not. + bool usesHardwareResolve = (userConfig.hardwareResolve != UserConfiguration::HardwareResolve::Disabled); + // Driver workarounds. - // - // Wireframe artifacts have been reported when using a high-precision color format on RDNA3 GPUs in D3D12. The workaround is to switch to Vulkan if this is the case. - bool isRDNA3 = device->getDescription().name.find("AMD Radeon RX 7") != std::string::npos; - bool useHDRinD3D12 = (userConfig.graphicsAPI == UserConfiguration::GraphicsAPI::D3D12) && (userConfig.internalColorFormat == UserConfiguration::InternalColorFormat::Automatic) && device->getCapabilities().preferHDR; - if (isRDNA3 && useHDRinD3D12) { - device.reset(); - renderInterface.reset(); - renderInterface = CreateVulkanInterface(); - if (renderInterface == nullptr) { - fprintf(stderr, "Unable to initialize graphics API.\n"); - return SetupResult::GraphicsAPINotFound; + const RenderDeviceVendor deviceVendor = device->getDescription().vendor; + const uint64_t driverVersion = device->getDescription().driverVersion; + if (deviceVendor == RenderDeviceVendor::NVIDIA) { + if (createdGraphicsAPI == UserConfiguration::GraphicsAPI::D3D12) { + if (usesHardwareResolve) { + // MSAA Resolve in D3D12 is broken since 565.90. During Resolve operations, the contents from unrelated graphics commands in a queue can leak to other commands + // being run in a different queue even if the resources aren't shared at all. The workaround is to disable hardware resolve and use rasterization instead. + const uint64_t BrokenNVIDIADriverD3D12 = 0x00200000000f19be; // 565.90 + const uint64_t FixedNVIDIADriverD3D12 = UINT64_MAX; // No driver is known to fix the issue at the moment. + if ((driverVersion >= BrokenNVIDIADriverD3D12) && (driverVersion < FixedNVIDIADriverD3D12) && (userConfig.hardwareResolve == UserConfiguration::HardwareResolve::Automatic)) { + usesHardwareResolve = false; + } + } } + } + else if (deviceVendor == RenderDeviceVendor::AMD) { + // Wireframe artifacts have been reported when using a high-precision color format on RDNA3 GPUs in D3D12. The workaround is to switch to Vulkan if this is the case. + bool isRDNA3 = device->getDescription().name.find("AMD Radeon RX 7") != std::string::npos; + bool useHDRinD3D12 = (userConfig.graphicsAPI == UserConfiguration::GraphicsAPI::D3D12) && (userConfig.internalColorFormat == UserConfiguration::InternalColorFormat::Automatic) && device->getCapabilities().preferHDR; + if (isRDNA3 && useHDRinD3D12) { + device.reset(); + renderInterface.reset(); + renderInterface = CreateVulkanInterface(); + if (renderInterface == nullptr) { + fprintf(stderr, "Unable to initialize graphics API.\n"); + return SetupResult::GraphicsAPINotFound; + } - createdGraphicsAPI = UserConfiguration::GraphicsAPI::Vulkan; + createdGraphicsAPI = UserConfiguration::GraphicsAPI::Vulkan; - device = renderInterface->createDevice(); - if (device == nullptr) { - fprintf(stderr, "Unable to find compatible graphics device.\n"); - return SetupResult::GraphicsDeviceNotFound; + device = renderInterface->createDevice(); + if (device == nullptr) { + fprintf(stderr, "Unable to find compatible graphics device.\n"); + return SetupResult::GraphicsDeviceNotFound; + } } } + // Detect if the application should use HDR framebuffers or not. + bool usesHDR; + switch (userConfig.internalColorFormat) { + case UserConfiguration::InternalColorFormat::High: + usesHDR = true; + break; + case UserConfiguration::InternalColorFormat::Automatic: + usesHDR = device->getCapabilities().preferHDR; + break; + case UserConfiguration::InternalColorFormat::Standard: + default: + usesHDR = false; + break; + } + // Call the init hook if one was attached. RenderHookInit *initHook = GetRenderHookInit(); if (initHook != nullptr) { @@ -201,21 +235,6 @@ namespace RT64 { // Create the swap chain with the buffer count specified from the configuration. const uint32_t bufferCount = (userConfig.displayBuffering == UserConfiguration::DisplayBuffering::Triple) ? 3 : 2; swapChain = presentGraphicsWorker->commandQueue->createSwapChain(appWindow->windowHandle, bufferCount, RenderFormat::B8G8R8A8_UNORM); - - // Detect if the application should use HDR framebuffers or not. - bool usesHDR; - switch (userConfig.internalColorFormat) { - case UserConfiguration::InternalColorFormat::High: - usesHDR = true; - break; - case UserConfiguration::InternalColorFormat::Automatic: - usesHDR = device->getCapabilities().preferHDR; - break; - case UserConfiguration::InternalColorFormat::Standard: - default: - usesHDR = false; - break; - } // Before configuring multisampling, make sure the device actually supports it for the formats we'll use. If it doesn't, turn off antialiasing in the configuration. const RenderSampleCounts colorSampleCounts = device->getSampleCountsSupported(RenderTarget::colorBufferFormat(usesHDR)); @@ -227,7 +246,7 @@ namespace RT64 { // Create the shader library. const RenderMultisampling multisampling = RasterShader::generateMultisamplingPattern(userConfig.msaaSampleCount(), device->getCapabilities().sampleLocations); - shaderLibrary = std::make_unique(usesHDR, userConfig.hardwareResolve); + shaderLibrary = std::make_unique(usesHDR, usesHardwareResolve); shaderLibrary->setupCommonShaders(renderInterface.get(), device.get()); shaderLibrary->setupMultisamplingShaders(renderInterface.get(), device.get(), multisampling); diff --git a/src/hle/rt64_state.cpp b/src/hle/rt64_state.cpp index 726e9e7..a751267 100644 --- a/src/hle/rt64_state.cpp +++ b/src/hle/rt64_state.cpp @@ -5,6 +5,7 @@ #include "rt64_state.h" #include +#include #include "im3d/im3d.h" #include "im3d/im3d_math.h" @@ -2080,14 +2081,14 @@ namespace RT64 { ImGui::Text("You must restart the application for this change to be applied."); } - genConfigChanged = ImGui::Checkbox("Three-Point Filtering", &userConfig.threePointFiltering) || genConfigChanged; - genConfigChanged = ImGui::Checkbox("High Performance State", &userConfig.idleWorkActive) || genConfigChanged; - - if (ImGui::Checkbox("Hardware Resolve", &userConfig.hardwareResolve)) { + if (ImGui::Combo("Hardware Resolve", reinterpret_cast(&userConfig.hardwareResolve), "Disabled\0Enabled\0Automatic\0")) { // Update shader library to automatically control all logic around hardware resolve. - ext.shaderLibrary->usesHardwareResolve = userConfig.hardwareResolve; + ext.shaderLibrary->usesHardwareResolve = (userConfig.hardwareResolve != UserConfiguration::HardwareResolve::Disabled); genConfigChanged = true; } + + genConfigChanged = ImGui::Checkbox("Three-Point Filtering", &userConfig.threePointFiltering) || genConfigChanged; + genConfigChanged = ImGui::Checkbox("High Performance State", &userConfig.idleWorkActive) || genConfigChanged; // Emulator configuration. ImGui::NewLine(); @@ -2430,12 +2431,15 @@ namespace RT64 { ImGui::NewLine(); + const RenderDeviceDescription &description = ext.device->getDescription(); const RenderDeviceCapabilities &capabilities = ext.device->getCapabilities(); ImGui::Text("Display Refresh Rate (OS): %d\n", ext.appWindow->getRefreshRate()); if (capabilities.displayTiming) { ImGui::Text("Display Refresh Rate (RHI): %d\n", ext.swapChain->getRefreshRate()); } + ImGui::Text("Vendor ID: 0x%08x", uint32_t(description.vendor)); + ImGui::Text("Driver Version: 0x%016" PRIx64, description.driverVersion); ImGui::Text("Raytracing: %d", capabilities.raytracing); ImGui::Text("Raytracing State Update: %d", capabilities.raytracingStateUpdate); ImGui::Text("Sample Locations: %d", capabilities.sampleLocations); diff --git a/src/rhi/rt64_render_interface_types.h b/src/rhi/rt64_render_interface_types.h index f52b647..3ee5229 100644 --- a/src/rhi/rt64_render_interface_types.h +++ b/src/rhi/rt64_render_interface_types.h @@ -63,6 +63,13 @@ namespace RT64 { // Enums. + enum class RenderDeviceVendor { + UNKNOWN = 0x0, + AMD = 0x1002, + NVIDIA = 0x10DE, + INTEL = 0x8086 + }; + enum class RenderFormat { UNKNOWN, R32G32B32A32_TYPELESS, @@ -1692,7 +1699,8 @@ namespace RT64 { struct RenderDeviceDescription { std::string name = "Unknown"; - uint32_t driverVersion = 0; + RenderDeviceVendor vendor = RenderDeviceVendor::UNKNOWN; + uint64_t driverVersion = 0; uint64_t dedicatedVideoMemory = 0; }; diff --git a/src/vulkan/rt64_vulkan.cpp b/src/vulkan/rt64_vulkan.cpp index b12451d..1d59900 100644 --- a/src/vulkan/rt64_vulkan.cpp +++ b/src/vulkan/rt64_vulkan.cpp @@ -3389,6 +3389,7 @@ namespace RT64 { physicalDevice = physicalDevices[i]; description.name = std::string(deviceProperties.deviceName); description.driverVersion = deviceProperties.driverVersion; + description.vendor = RenderDeviceVendor(deviceProperties.vendorID); currentDeviceTypeScore = deviceTypeScore; } }