2

I have sample code that creates a 320x180 texture and displays it in a resizable window that starts off at 320x180 inner-size.

But as I resize the window upwards, the texture is blurry. I thought that using D3D11_FILTER_MIN_MAG_MIP_POINT would be enough to get a pixelated effect, but it's not. What am I missing?

Here's an example of the window at 320x180 and also resized bigger:

enter image description here

enter image description here

And here's the entire reproducable sample code:

Compile in PowerShell with (cl .\cpu.cpp) -and (./cpu.exe)

// gpu.hlsl

struct pixeldesc
{
    float4 position : SV_POSITION;
    float2 texcoord : TEX;
};

Texture2D    mytexture : register(t0);
SamplerState mysampler : register(s0);

pixeldesc VsMain(uint vI : SV_VERTEXID)
{
    pixeldesc output;
    output.texcoord = float2(vI % 2, vI / 2);
    output.position = float4(output.texcoord * float2(2, -2) - float2(1, -1), 0, 1);
    return output;
}

float4 PsMain(pixeldesc pixel) : SV_TARGET
{
    return float4(mytexture.Sample(mysampler, pixel.texcoord).rgb, 1);
}
// cpu.cpp

#pragma comment(lib, "user32")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d3dcompiler")

#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>

int winw = 320*1;
int winh = 180*1;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_DESTROY) {
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
    WNDCLASSA wndclass = { 0, WindowProc, 0, 0, 0, 0, 0, 0, 0, "d8" };

    RegisterClassA(&wndclass);

    RECT winbox;
    winbox.left = GetSystemMetrics(SM_CXSCREEN) / 2 - winw / 2;
    winbox.top = GetSystemMetrics(SM_CYSCREEN) / 2 - winh / 2;
    winbox.right = winbox.left + winw;
    winbox.bottom = winbox.top + winh;
    AdjustWindowRectEx(&winbox, WS_OVERLAPPEDWINDOW, false, 0);

    HWND window = CreateWindowExA(0, "d8", "testing d3d11 upscaling", WS_OVERLAPPEDWINDOW|WS_VISIBLE, 
        winbox.left,
        winbox.top,
        winbox.right - winbox.left,
        winbox.bottom - winbox.top,
        0, 0, 0, 0);

    D3D_FEATURE_LEVEL featurelevels[] = { D3D_FEATURE_LEVEL_11_0 };

    DXGI_SWAP_CHAIN_DESC swapchaindesc = {};
    swapchaindesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapchaindesc.SampleDesc.Count  = 1;
    swapchaindesc.BufferUsage       = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapchaindesc.BufferCount       = 2;
    swapchaindesc.OutputWindow      = window;
    swapchaindesc.Windowed          = TRUE;
    swapchaindesc.SwapEffect        = DXGI_SWAP_EFFECT_FLIP_DISCARD;

    IDXGISwapChain* swapchain;

    ID3D11Device* device;
    ID3D11DeviceContext* devicecontext;

    D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featurelevels, ARRAYSIZE(featurelevels), D3D11_SDK_VERSION, &swapchaindesc, &swapchain, &device, nullptr, &devicecontext);

    ID3D11Texture2D* framebuffer;
    swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&framebuffer); // get the swapchain's buffer

    ID3D11RenderTargetView* framebufferRTV;
    device->CreateRenderTargetView(framebuffer, nullptr, &framebufferRTV); // and make it a render target [view]

    ID3DBlob* vertexshaderCSO;
    D3DCompileFromFile(L"gpu.hlsl", 0, 0, "VsMain", "vs_5_0", 0, 0, &vertexshaderCSO, 0);
    ID3D11VertexShader* vertexshader;
    device->CreateVertexShader(vertexshaderCSO->GetBufferPointer(), vertexshaderCSO->GetBufferSize(), 0, &vertexshader);

    ID3DBlob* pixelshaderCSO;
    D3DCompileFromFile(L"gpu.hlsl", 0, 0, "PsMain", "ps_5_0", 0, 0, &pixelshaderCSO, 0);
    ID3D11PixelShader* pixelshader;
    device->CreatePixelShader(pixelshaderCSO->GetBufferPointer(), pixelshaderCSO->GetBufferSize(), 0, &pixelshader);

    D3D11_RASTERIZER_DESC rasterizerdesc = { D3D11_FILL_SOLID, D3D11_CULL_NONE };
    ID3D11RasterizerState* rasterizerstate;
    device->CreateRasterizerState(&rasterizerdesc, &rasterizerstate);

    D3D11_SAMPLER_DESC samplerdesc = { D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP };
    ID3D11SamplerState* samplerstate;
    device->CreateSamplerState(&samplerdesc, &samplerstate);

    unsigned char texturedata[320*180*4];
    for (int i = 0; i < 320*180*4; i++) {
        texturedata[i] = rand() % 0xff;
    }

    D3D11_TEXTURE2D_DESC texturedesc = {};
    texturedesc.Width            = 320;
    texturedesc.Height           = 180;
    texturedesc.MipLevels        = 1;
    texturedesc.ArraySize        = 1;
    texturedesc.Format           = DXGI_FORMAT_R8G8B8A8_UNORM;
    texturedesc.SampleDesc.Count = 1;
    texturedesc.Usage            = D3D11_USAGE_IMMUTABLE;
    texturedesc.BindFlags        = D3D11_BIND_SHADER_RESOURCE;

    D3D11_SUBRESOURCE_DATA textureSRD = {};
    textureSRD.pSysMem     = texturedata;
    textureSRD.SysMemPitch = 320 * 4;

    ID3D11Texture2D* texture;
    device->CreateTexture2D(&texturedesc, &textureSRD, &texture);

    ID3D11ShaderResourceView* textureSRV;
    device->CreateShaderResourceView(texture, nullptr, &textureSRV);

    D3D11_VIEWPORT viewport = { 0, 0, winw, winh, 0, 1 };

    MSG msg = { 0 };
    while (msg.message != WM_QUIT) {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            devicecontext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

            devicecontext->VSSetShader(vertexshader, nullptr, 0);

            devicecontext->RSSetViewports(1, &viewport);
            devicecontext->RSSetState(rasterizerstate);

            devicecontext->PSSetShader(pixelshader, nullptr, 0);
            devicecontext->PSSetShaderResources(0, 1, &textureSRV);
            devicecontext->PSSetSamplers(0, 1, &samplerstate);

            devicecontext->OMSetRenderTargets(1, &framebufferRTV, nullptr);

            devicecontext->Draw(4, 0);

            swapchain->Present(1, 0);
        }
    }
}
7
  • 3
    What do you expect? You're upscaling a small image (texture) no information is being added. MipMaps work the other way around, they can scale down to smaller textures when an object is far away (reducing memory bandwidth) Commented Jul 9 at 15:56
  • 1
    @PepijnKramer then what is the solution if not mipmaps? Commented Jul 9 at 16:02
  • 2
    as Pepijn said, what do you expect to happen? Its not obvious how to make a bigger image from the 320x180 image (other than what you already got). If you know what you want to happen a solution can be found Commented Jul 9 at 16:30
  • 1
    The solution is to start out with a much larger texture with more detail. (Not even AI upscaling is going to help you here) Commented Jul 9 at 16:50
  • 1
    @PepijnKramer when I was using SDL with the D3D11 backend, it was able to do this same concept just fine. I'm looking through their code to see what they do. Apparently when the window resizes, they recreate some resources, but not the texture itself, which can stay 320x180. Commented Jul 9 at 17:56

1 Answer 1

1

Got it working!

Based on https://www.gamedev.net/forums/topic/623652-how-should-i-resize-a-directx-11-window/4933002/ and How to Change Window Size in DirectX 11 Desktop Application I was able to understand the solution:

When the window resizes, we have to release and recreate the framebuffer and framebuffer render target view:

    if (uMsg == WM_SIZE) {
        UINT width = LOWORD(lParam);
        UINT height = HIWORD(lParam);
        printf("size %d , %d\n", width, height);

        if (first) {
            first = 0;
            return 0;
        }

        winw = width;
        winh = height;

        viewport.Width = winw;
        viewport.Height = winh;
        devicecontext->RSSetViewports(1, &viewport);

        devicecontext->Flush();

        framebufferRTV->Release();
        framebuffer->Release();

        swapchain->ResizeBuffers(0, winw, winh, DXGI_FORMAT_UNKNOWN, 0);

        swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&framebuffer); // get the swapchain's buffer
        device->CreateRenderTargetView(framebuffer, nullptr, &framebufferRTV); // and make it a render target [view]
   
        return 0;
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.