skyrim-vr-upscale / bench /perf_hlsl.cpp
phanerozoic's picture
source: kernel, HLSL in-game path, OpenVR integration, benches
5506acb verified
Raw
History Blame
8.46 kB
// perf2.cpp — native (HLSL compute) SVR upscale performance, no CUDA/interop.
// Renders the expensive scene to eye targets at full res and at renderScale R
// (D3D timestamp timed), then reconstructs R -> full with the HLSL compute
// port of the kernel (also D3D timestamp timed, same clock). Reports the net
// frame speedup against the pixel-count ceiling. This is the in-game path:
// everything stays in D3D11, so there is no bridge tax to pay.
#include <d3d11.h>
#include <d3dcompiler.h>
#include <cstdint>
#include <cstdio>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")
static const char* kScene = R"(
cbuffer CB : register(b0) { float4 p; }
struct VSOut { float4 pos : SV_Position; float2 uv : TEXCOORD0; };
VSOut vs(uint id : SV_VertexID){ VSOut o; float2 xy=float2((id<<1)&2,id&2);
o.pos=float4(xy*2.0-1.0,0,1); o.uv=xy; return o; }
float hash(float2 n){ return frac(sin(dot(n,float2(12.9898,78.233)))*43758.5453); }
float vn(float2 x){ float2 i=floor(x),f=frac(x); f=f*f*(3-2*f);
float a=hash(i),b=hash(i+float2(1,0)),c=hash(i+float2(0,1)),d=hash(i+float2(1,1));
return lerp(lerp(a,b,f.x),lerp(c,d,f.x),f.y); }
float4 ps(VSOut v):SV_Target{ float2 uv=v.uv*p.yz*0.02; float2 q=uv;
float acc=0,amp=0.5; int N=(int)p.x;
[loop] for(int i=0;i<N;i++){ q+=float2(vn(q*1.7+p.w),vn(q*1.7-p.w))*0.35;
acc+=amp*vn(q); amp*=0.6; q*=1.9; }
return float4(0.5+0.5*cos(6.2831*(acc+float3(0,0.33,0.67))),1); }
)";
static ID3DBlob* compile(const char* src, const char* entry, const char* tgt) {
ID3DBlob *b = nullptr, *e = nullptr;
if (FAILED(D3DCompile(src, strlen(src), 0, 0, 0, entry, tgt, 0, 0, &b, &e))) {
printf("compile %s: %s\n", entry, e ? (char*)e->GetBufferPointer() : "?");
return nullptr;
}
return b;
}
static char* slurp(const char* path) {
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
char* s = (char*)malloc(n + 1); fread(s, 1, n, f); s[n] = 0; fclose(f);
return s;
}
struct RT {
ID3D11Texture2D* tex = nullptr;
ID3D11RenderTargetView* rtv = nullptr;
ID3D11ShaderResourceView* srv = nullptr;
ID3D11UnorderedAccessView* uav = nullptr;
};
static RT mkrt(ID3D11Device* d, int w, int h) {
RT r;
D3D11_TEXTURE2D_DESC td = {};
td.Width = w; td.Height = h; td.MipLevels = 1; td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM; td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE |
D3D11_BIND_UNORDERED_ACCESS;
d->CreateTexture2D(&td, nullptr, &r.tex);
d->CreateRenderTargetView(r.tex, nullptr, &r.rtv);
d->CreateShaderResourceView(r.tex, nullptr, &r.srv);
d->CreateUnorderedAccessView(r.tex, nullptr, &r.uav);
return r;
}
struct SvrCB { uint32_t sw, sh, dw, dh; float cx, cy, r0, r1; float sharp, p0, p1, p2; };
int main() {
ID3D11Device* dev = nullptr; ID3D11DeviceContext* ctx = nullptr;
D3D_FEATURE_LEVEL fl;
if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
nullptr, 0, D3D11_SDK_VERSION, &dev, &fl, &ctx))) {
printf("device create failed\n"); return 1; }
ID3DBlob* vsb = compile(kScene, "vs", "vs_5_0");
ID3DBlob* psb = compile(kScene, "ps", "ps_5_0");
if (!vsb || !psb) return 1;
ID3D11VertexShader* vs = nullptr; ID3D11PixelShader* ps = nullptr;
dev->CreateVertexShader(vsb->GetBufferPointer(), vsb->GetBufferSize(), 0, &vs);
dev->CreatePixelShader(psb->GetBufferPointer(), psb->GetBufferSize(), 0, &ps);
// Resolve the shader from SVR_HLSL, then the repo-relative locations.
const char* cands[] = { getenv("SVR_HLSL"), "svr_upscale.hlsl",
"..\\game\\svr_upscale.hlsl", "game\\svr_upscale.hlsl" };
char* csrc = nullptr;
for (const char* c : cands) if (c && (csrc = slurp(c))) break;
if (!csrc) { printf("cannot find svr_upscale.hlsl (set SVR_HLSL)\n"); return 1; }
ID3DBlob* csb = compile(csrc, "main", "cs_5_0");
if (!csb) return 1;
ID3D11ComputeShader* cs = nullptr;
dev->CreateComputeShader(csb->GetBufferPointer(), csb->GetBufferSize(), 0, &cs);
D3D11_BUFFER_DESC bd = {}; bd.ByteWidth = 16; bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
ID3D11Buffer* scb = nullptr; dev->CreateBuffer(&bd, nullptr, &scb);
bd.ByteWidth = sizeof(SvrCB);
ID3D11Buffer* ucb = nullptr; dev->CreateBuffer(&bd, nullptr, &ucb);
D3D11_RASTERIZER_DESC rd = {}; rd.FillMode = D3D11_FILL_SOLID; rd.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rs = nullptr; dev->CreateRasterizerState(&rd, &rs);
ID3D11Query *qd = nullptr, *q0 = nullptr, *q1 = nullptr;
D3D11_QUERY_DESC dd = {D3D11_QUERY_TIMESTAMP_DISJOINT, 0}; dev->CreateQuery(&dd, &qd);
D3D11_QUERY_DESC tq = {D3D11_QUERY_TIMESTAMP, 0};
dev->CreateQuery(&tq, &q0); dev->CreateQuery(&tq, &q1);
auto scene = [&](RT& L, RT& R, int w, int h, int oct) {
float p[4] = {(float)oct, (float)w, (float)h, 1.3f};
ctx->UpdateSubresource(scb, 0, 0, p, 0, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->IASetInputLayout(nullptr);
ctx->VSSetShader(vs, 0, 0); ctx->VSSetConstantBuffers(0, 1, &scb);
ctx->PSSetShader(ps, 0, 0); ctx->PSSetConstantBuffers(0, 1, &scb);
ctx->RSSetState(rs);
D3D11_VIEWPORT vp = {0, 0, (float)w, (float)h, 0, 1}; ctx->RSSetViewports(1, &vp);
RT* e[2] = {&L, &R};
for (int i = 0; i < 2; ++i) { ctx->OMSetRenderTargets(1, &e[i]->rtv, nullptr); ctx->Draw(3, 0); }
};
auto upscale = [&](RT& sL, RT& sR, int iw, int ih, RT& dL, RT& dR, int ow, int oh) {
SvrCB u = {(uint32_t)iw,(uint32_t)ih,(uint32_t)ow,(uint32_t)oh,
0.5f*ow,0.5f*oh, 0.55f*0.65f*oh, 0.65f*oh, 0.4f, 0,0,0};
ctx->CSSetShader(cs, 0, 0);
RT* s[2] = {&sL, &sR}; RT* d[2] = {&dL, &dR};
float cxs[2] = {0.46f, 0.54f};
for (int i = 0; i < 2; ++i) {
u.cx = cxs[i] * ow;
ctx->UpdateSubresource(ucb, 0, 0, &u, 0, 0);
ctx->CSSetConstantBuffers(0, 1, &ucb);
ctx->CSSetShaderResources(0, 1, &s[i]->srv);
ID3D11UnorderedAccessView* uav = d[i]->uav;
ctx->CSSetUnorderedAccessViews(0, 1, &uav, nullptr);
ctx->Dispatch((ow + 7) / 8, (oh + 7) / 8, 1);
ID3D11UnorderedAccessView* nu = nullptr;
ctx->CSSetUnorderedAccessViews(0, 1, &nu, nullptr);
}
};
auto gpu_ms = [&](auto&& fn) -> double {
for (int i = 0; i < 3; ++i) fn(); ctx->Flush();
double best = 1e30;
for (int rep = 0; rep < 12; ++rep) {
ctx->Begin(qd); ctx->End(q0); fn(); ctx->End(q1); ctx->End(qd); ctx->Flush();
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; UINT64 a, b;
while (ctx->GetData(qd, &dj, sizeof(dj), 0) != S_OK) {}
while (ctx->GetData(q0, &a, sizeof(a), 0) != S_OK) {}
while (ctx->GetData(q1, &b, sizeof(b), 0) != S_OK) {}
if (dj.Disjoint) continue;
double ms = 1000.0 * (double)(b - a) / (double)dj.Frequency;
if (ms < best) best = ms;
}
return best;
};
struct Res { int w, h; const char* name; };
Res shapes[] = { {1440,1600,"Quest3 low"}, {2016,2240,"Quest3 med"}, {2464,2736,"Quest3 high"} };
float scales[] = { 0.90f, 0.77f, 0.67f, 0.59f };
int octs[] = { 8, 16, 24 };
printf("Native HLSL SVR upscale, no CUDA/interop (GPU timestamps, per frame = 2 eyes)\n\n");
for (auto& sh : shapes) {
RT fL = mkrt(dev, sh.w, sh.h), fR = mkrt(dev, sh.w, sh.h);
printf("== %s %dx%d/eye ==\n", sh.name, sh.w, sh.h);
printf("%6s %7s | %8s %9s %8s %8s | %8s %8s\n",
"scale","pix%","full ms","scaled ms","upsc ms","frame ms","speedup","ceiling");
for (float sc : scales) {
int rw = (int)(sh.w * sc + 0.5f), rh = (int)(sh.h * sc + 0.5f);
RT rL = mkrt(dev, rw, rh), rR = mkrt(dev, rw, rh);
double pixr = (double)(rw * rh) / (double)(sh.w * sh.h);
for (int oct : octs) {
double tf = gpu_ms([&]{ scene(fL, fR, sh.w, sh.h, oct); });
double ts = gpu_ms([&]{ scene(rL, rR, rw, rh, oct); });
double tu = gpu_ms([&]{ upscale(rL, rR, rw, rh, fL, fR, sh.w, sh.h); });
double frame = ts + tu;
printf("%5.2f %6.1f%% | %8.3f %9.3f %8.3f %8.3f | %7.2fx %7.2fx (oct %d)\n",
sc, 100.0 * pixr, tf, ts, tu, frame, tf / frame, 1.0 / pixr, oct);
}
rL.tex->Release(); rR.tex->Release();
}
fL.tex->Release(); fR.tex->Release();
printf("\n");
}
printf("done\n");
return 0;
}