Skip to content

Commit

Permalink
Implement MIP LOD bias for upscaling
Browse files Browse the repository at this point in the history
  • Loading branch information
fholger committed Jan 4, 2022
1 parent cb468d5 commit dab654e
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
103 changes: 89 additions & 14 deletions src/d3d11/d3d11_post_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,100 @@

#include "config.h"
#include "d3d11_fsr_upscaler.h"
#include "logging.h"
#include "hooks.h"

namespace vrperfkit {
D3D11PostProcessor::D3D11PostProcessor(ComPtr<ID3D11Device> device) : device(device) {
namespace {
void D3D11ContextHook_PSSetSamplers(ID3D11DeviceContext *self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState * const *ppSamplers) {
ID3D11SamplerState *samplers[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT];
memcpy(samplers, ppSamplers, NumSamplers * sizeof(ID3D11SamplerState*));

D3D11PostProcessor *postProcessor = nullptr;
UINT size = sizeof(postProcessor);
if (SUCCEEDED(self->GetPrivateData(__uuidof(D3D11PostProcessor), &size, &postProcessor)) && postProcessor != nullptr) {
postProcessor->OnPSSetSamplers(samplers, NumSamplers);
}

hooks::CallOriginal(D3D11ContextHook_PSSetSamplers)(self, StartSlot, NumSamplers, samplers);
}
}

D3D11PostProcessor::D3D11PostProcessor(ComPtr<ID3D11Device> device) : device(device) {}

D3D11PostProcessor::~D3D11PostProcessor() {
ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(context.GetAddressOf());
context->SetPrivateData(__uuidof(D3D11PostProcessor), sizeof(this), this);
context->SetPrivateData(__uuidof(D3D11PostProcessor), 0, nullptr);
}

bool D3D11PostProcessor::Apply(const D3D11PostProcessInput &input, Viewport &outputViewport) {
if (g_config.upscaling.enabled) {
PrepareUpscaler(input.outputTexture);
D3D11_TEXTURE2D_DESC td;
input.outputTexture->GetDesc(&td);
outputViewport.x = outputViewport.y = 0;
outputViewport.width = td.Width;
outputViewport.height = td.Height;
if (input.mode == TextureMode::COMBINED) {
outputViewport.width /= 2;
if (input.eye == RIGHT_EYE) {
outputViewport.x += outputViewport.width;
try {
PrepareUpscaler(input.outputTexture);
D3D11_TEXTURE2D_DESC td;
input.outputTexture->GetDesc(&td);
outputViewport.x = outputViewport.y = 0;
outputViewport.width = td.Width;
outputViewport.height = td.Height;
if (input.mode == TextureMode::COMBINED) {
outputViewport.width /= 2;
if (input.eye == RIGHT_EYE) {
outputViewport.x += outputViewport.width;
}
}
upscaler->Upscale(input, outputViewport);

float newLodBias = -log2f(outputViewport.width / (float)input.inputViewport.width);
if (newLodBias != mipLodBias) {
LOG_DEBUG << "MIP LOD Bias changed from " << mipLodBias << " to " << newLodBias << ", recreating samplers";
passThroughSamplers.clear();
mappedSamplers.clear();
mipLodBias = newLodBias;
}

return true;
}
catch (const std::exception &e) {
LOG_ERROR << "Upscaling failed: " << e.what();
g_config.upscaling.enabled = false;
}
upscaler->Upscale(input, outputViewport);
return true;
}
return false;
}

void D3D11PostProcessor::OnPSSetSamplers(ID3D11SamplerState **samplers, UINT numSamplers) {
if (!g_config.upscaling.applyMipBias) {
passThroughSamplers.clear();
mappedSamplers.clear();
return;
}

for (UINT i = 0; i < numSamplers; ++i) {
ID3D11SamplerState *orig = samplers[i];
if (orig == nullptr || passThroughSamplers.find(orig) != passThroughSamplers.end()) {
continue;
}

if (mappedSamplers.find(orig) == mappedSamplers.end()) {
D3D11_SAMPLER_DESC sd;
orig->GetDesc(&sd);
if (sd.MipLODBias != 0 || sd.MaxAnisotropy == 1) {
// do not mess with samplers that already have a bias or are not doing anisotropic filtering.
// should hopefully reduce the chance of causing rendering errors.
passThroughSamplers.insert(orig);
continue;
}
sd.MipLODBias = mipLodBias;
LOG_INFO << "Creating replacement sampler for " << orig << " with MIP LOD bias " << sd.MipLODBias;
device->CreateSamplerState(&sd, mappedSamplers[orig].GetAddressOf());
passThroughSamplers.insert(mappedSamplers[orig].Get());
}

samplers[i] = mappedSamplers[orig].Get();
}
}

void D3D11PostProcessor::PrepareUpscaler(ID3D11Texture2D *outputTexture) {
if (upscaler == nullptr || upscaleMethod != g_config.upscaling.method) {
D3D11_TEXTURE2D_DESC td;
Expand All @@ -44,6 +110,15 @@ namespace vrperfkit {
upscaler.reset(new D3D11FsrUpscaler(device.Get(), td.Width, td.Height, td.Format));
break;
}

passThroughSamplers.clear();
mappedSamplers.clear();
ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(context.GetAddressOf());
D3D11PostProcessor *instance = this;
UINT size = sizeof(instance);
context->SetPrivateData(__uuidof(D3D11PostProcessor), size, &instance);
hooks::InstallVirtualFunctionHook("PSSetSamplers", context.Get(), 10, (void*)&D3D11ContextHook_PSSetSamplers);
}
}
}
10 changes: 10 additions & 0 deletions src/d3d11/d3d11_post_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "d3d11_helper.h"

#include <memory>
#include <unordered_map>
#include <unordered_set>

namespace vrperfkit {
struct D3D11PostProcessInput {
Expand All @@ -25,13 +27,21 @@ namespace vrperfkit {
class __declspec(uuid("eca6ea48-d763-48b9-8181-4e316335ad97")) D3D11PostProcessor {
public:
D3D11PostProcessor(ComPtr<ID3D11Device> device);
~D3D11PostProcessor();

bool Apply(const D3D11PostProcessInput &input, Viewport &outputViewport);

void OnPSSetSamplers(ID3D11SamplerState **samplers, UINT numSamplers);

private:
ComPtr<ID3D11Device> device;
std::unique_ptr<D3D11Upscaler> upscaler;
UpscaleMethod upscaleMethod;

void PrepareUpscaler(ID3D11Texture2D *outputTexture);

std::unordered_set<ID3D11SamplerState*> passThroughSamplers;
std::unordered_map<ID3D11SamplerState*, ComPtr<ID3D11SamplerState>> mappedSamplers;
float mipLodBias = 0.0f;
};
}
9 changes: 7 additions & 2 deletions src/hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ namespace vrperfkit {
LPVOID pTarget = vtable[methodPos];

LPVOID pOriginal = nullptr;
if (MH_CreateHook(pTarget, detour, &pOriginal) != MH_OK || MH_EnableHook(pTarget) != MH_OK) {
LOG_ERROR << "Failed to install hook for " << name;
MH_STATUS result = MH_CreateHook(pTarget, detour, &pOriginal);
if (result != MH_OK || MH_EnableHook(pTarget) != MH_OK) {
if (result == MH_ERROR_ALREADY_CREATED) {
LOG_INFO << " Hook already installed.";
} else {
LOG_ERROR << "Failed to install hook for " << name;
}
return;
}

Expand Down
6 changes: 6 additions & 0 deletions src/hotkeys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ namespace {
LOG_INFO << "Debug mode is now " << (g_config.debugMode ? "enabled" : "disabled");
}

void ToggleUpscalingApplyMipBias() {
g_config.upscaling.applyMipBias = !g_config.upscaling.applyMipBias;
LOG_INFO << "MIP LOD bias is now " << (g_config.upscaling.applyMipBias ? "enabled" : "disabled");
}

struct HotkeyDefinition {
std::string name;
std::function<void()> action;
Expand All @@ -50,6 +55,7 @@ namespace {
{"increaseUpscalingSharpness", IncreaseUpscalingSharpness},
{"decreaseUpscalingSharpness", DecreaseUpscalingSharpness},
{"toggleDebugMode", ToggleDebugMode},
{"toggleUpscalingApplyMipBias", ToggleUpscalingApplyMipBias},
};
}

Expand Down
1 change: 1 addition & 0 deletions vrperfkit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ hotkeys:
decreaseUpscalingRadius: ["ctrl", "f3"]
increaseUpscalingSharpness: ["ctrl", "f4"]
decreaseUpscalingSharpness: ["ctrl", "f5"]
toggleUpscalingApplyMipBias: ["ctrl", "f6"]

0 comments on commit dab654e

Please sign in to comment.