mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
Apparently, these two system values ignore the base vertex and base instance from the draw call. This is not documented, but in line with what the AMD driver does on Windows.
406 lines
13 KiB
C++
406 lines
13 KiB
C++
#include <d3dcompiler.h>
|
|
#include <d3d11.h>
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
|
|
#include "../test_utils.h"
|
|
|
|
using namespace dxvk;
|
|
|
|
struct Extent2D {
|
|
uint32_t w, h;
|
|
};
|
|
|
|
struct Vertex {
|
|
float x, y, z, w;
|
|
};
|
|
|
|
const std::string g_vertexShaderCode =
|
|
"struct vs_out {\n"
|
|
" float4 pos : SV_POSITION;\n"
|
|
" uint vid : VID;\n"
|
|
" uint iid : IID;\n"
|
|
"};\n"
|
|
"vs_out main(float4 vsIn : IN_POSITION,\n"
|
|
" uint vid : SV_VERTEXID,\n"
|
|
" uint iid : SV_INSTANCEID) {\n"
|
|
" vs_out result;\n"
|
|
" result.pos = vsIn;\n"
|
|
" result.vid = vid;\n"
|
|
" result.iid = iid;\n"
|
|
" return result;\n"
|
|
"}\n";
|
|
|
|
const std::string g_pixelShaderCode =
|
|
"struct vs_out {\n"
|
|
" float4 pos : SV_POSITION;\n"
|
|
" uint vid : VID;\n"
|
|
" uint iid : IID;\n"
|
|
"};\n"
|
|
"cbuffer c_buffer { float4 ccolor[2]; };\n"
|
|
"float4 main(vs_out ps_in) : SV_TARGET {\n"
|
|
" return 0.5f * (ccolor[min(ps_in.vid, 1u)] + ccolor[min(ps_in.iid, 1u)]);\n"
|
|
"}\n";
|
|
|
|
class TriangleApp {
|
|
|
|
public:
|
|
|
|
TriangleApp(HINSTANCE instance, HWND window)
|
|
: m_window(window) {
|
|
if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory),
|
|
reinterpret_cast<void**>(&m_factory))))
|
|
throw DxvkError("Failed to create DXGI factory");
|
|
|
|
if (FAILED(m_factory->EnumAdapters(0, &m_adapter)))
|
|
throw DxvkError("Failed to enumerate DXGI adapter");
|
|
|
|
HRESULT status = D3D11CreateDevice(
|
|
m_adapter.ptr(),
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
nullptr, 0,
|
|
nullptr, 0,
|
|
D3D11_SDK_VERSION,
|
|
&m_device,
|
|
&m_featureLevel,
|
|
&m_context);
|
|
|
|
if (FAILED(status))
|
|
throw DxvkError("Failed to create D3D11 device");
|
|
|
|
DXGI_SWAP_CHAIN_DESC swapDesc;
|
|
swapDesc.BufferDesc.Width = m_windowSize.w;
|
|
swapDesc.BufferDesc.Height = m_windowSize.h;
|
|
swapDesc.BufferDesc.RefreshRate = { 60, 1 };
|
|
swapDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
swapDesc.SampleDesc.Count = 8;
|
|
swapDesc.SampleDesc.Quality = 0;
|
|
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapDesc.BufferCount = 2;
|
|
swapDesc.OutputWindow = window;
|
|
swapDesc.Windowed = true;
|
|
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
swapDesc.Flags = 0;
|
|
|
|
if (FAILED(m_factory->CreateSwapChain(m_device.ptr(), &swapDesc, &m_swapChain)))
|
|
throw DxvkError("Failed to create DXGI swap chain");
|
|
|
|
if (FAILED(m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&m_buffer))))
|
|
throw DxvkError("Failed to get swap chain back buffer");
|
|
|
|
if (FAILED(m_device->CreateRenderTargetView(m_buffer.ptr(), nullptr, &m_bufferView)))
|
|
throw DxvkError("Failed to create render target view");
|
|
|
|
if (FAILED(m_swapChain->ResizeTarget(&swapDesc.BufferDesc)))
|
|
throw DxvkError("Failed to resize window");
|
|
|
|
std::array<Vertex, 21> vertexData = {{
|
|
{ -0.25f, -0.15f, 0.0f, 1.0f },
|
|
{ -0.50f, -0.65f, 0.0f, 1.0f },
|
|
{ -0.75f, -0.15f, 0.0f, 1.0f },
|
|
{ 0.75f, -0.15f, 0.0f, 1.0f },
|
|
{ 0.50f, -0.65f, 0.0f, 1.0f },
|
|
{ 0.25f, -0.15f, 0.0f, 1.0f },
|
|
{ -0.75f, 0.15f, 0.0f, 1.0f },
|
|
{ -0.50f, 0.65f, 0.0f, 1.0f },
|
|
{ -0.25f, 0.15f, 0.0f, 1.0f },
|
|
{ 0.25f, 0.15f, 0.0f, 1.0f },
|
|
{ 0.50f, 0.65f, 0.0f, 1.0f },
|
|
{ 0.75f, 0.15f, 0.0f, 1.0f },
|
|
{ 0.25f, 0.75f, 0.0f, 1.0f },
|
|
{ 0.00f, 0.25f, 0.0f, 1.0f },
|
|
{ -0.25f, 0.75f, 0.0f, 1.0f },
|
|
{ -0.25f, -0.75f, 0.0f, 1.0f },
|
|
{ 0.00f, -0.25f, 0.0f, 1.0f },
|
|
{ 0.25f, -0.75f, 0.0f, 1.0f },
|
|
{ -0.25f, -0.25f, 0.0f, 1.0f },
|
|
{ 0.00f, 0.25f, 0.0f, 1.0f },
|
|
{ 0.25f, -0.25f, 0.0f, 1.0f },
|
|
}};
|
|
|
|
D3D11_BUFFER_DESC vertexDesc;
|
|
vertexDesc.ByteWidth = sizeof(Vertex) * vertexData.size();
|
|
vertexDesc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
|
vertexDesc.CPUAccessFlags = 0;
|
|
vertexDesc.MiscFlags = 0;
|
|
vertexDesc.StructureByteStride = 0;
|
|
|
|
D3D11_SUBRESOURCE_DATA vertexDataInfo;
|
|
vertexDataInfo.pSysMem = vertexData.data();
|
|
vertexDataInfo.SysMemPitch = 0;
|
|
vertexDataInfo.SysMemSlicePitch = 0;
|
|
|
|
if (FAILED(m_device->CreateBuffer(&vertexDesc, &vertexDataInfo, &m_vertexBuffer)))
|
|
throw DxvkError("Failed to create vertex buffer");
|
|
|
|
std::array<uint32_t, 9> indexData = {{
|
|
0, 1, 2, 0, 1, 2, 2, 1, 0
|
|
}};
|
|
|
|
D3D11_BUFFER_DESC indexDesc;
|
|
indexDesc.ByteWidth = sizeof(uint32_t) * indexData.size();
|
|
indexDesc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
|
indexDesc.CPUAccessFlags = 0;
|
|
indexDesc.MiscFlags = 0;
|
|
indexDesc.StructureByteStride = 0;
|
|
|
|
D3D11_SUBRESOURCE_DATA indexDataInfo;
|
|
indexDataInfo.pSysMem = indexData.data();
|
|
indexDataInfo.SysMemPitch = 0;
|
|
indexDataInfo.SysMemSlicePitch = 0;
|
|
|
|
if (FAILED(m_device->CreateBuffer(&indexDesc, &indexDataInfo, &m_indexBuffer)))
|
|
throw DxvkError("Failed to create index buffer");
|
|
|
|
std::array<Vertex, 2> constantData = {{
|
|
{ 0.03f, 0.03f, 0.03f, 1.0f },
|
|
{ 1.00f, 0.00f, 0.00f, 1.0f },
|
|
}};
|
|
|
|
D3D11_BUFFER_DESC constantDesc;
|
|
constantDesc.ByteWidth = sizeof(Vertex) * constantData.size();
|
|
constantDesc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
constantDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
|
constantDesc.CPUAccessFlags = 0;
|
|
constantDesc.MiscFlags = 0;
|
|
constantDesc.StructureByteStride = 0;
|
|
|
|
D3D11_SUBRESOURCE_DATA constantDataInfo;
|
|
constantDataInfo.pSysMem = constantData.data();
|
|
constantDataInfo.SysMemPitch = 0;
|
|
constantDataInfo.SysMemSlicePitch = 0;
|
|
|
|
if (FAILED(m_device->CreateBuffer(&constantDesc, &constantDataInfo, &m_constantBuffer)))
|
|
throw DxvkError("Failed to create constant buffer");
|
|
|
|
Com<ID3DBlob> vertexShaderBlob;
|
|
Com<ID3DBlob> pixelShaderBlob;
|
|
|
|
if (FAILED(D3DCompile(
|
|
g_vertexShaderCode.data(),
|
|
g_vertexShaderCode.size(),
|
|
"Vertex shader",
|
|
nullptr, nullptr,
|
|
"main", "vs_5_0", 0, 0,
|
|
&vertexShaderBlob,
|
|
nullptr)))
|
|
throw DxvkError("Failed to compile vertex shader");
|
|
|
|
if (FAILED(D3DCompile(
|
|
g_pixelShaderCode.data(),
|
|
g_pixelShaderCode.size(),
|
|
"Pixel shader",
|
|
nullptr, nullptr,
|
|
"main", "ps_5_0", 0, 0,
|
|
&pixelShaderBlob,
|
|
nullptr)))
|
|
throw DxvkError("Failed to compile pixel shader");
|
|
|
|
if (FAILED(m_device->CreateVertexShader(
|
|
vertexShaderBlob->GetBufferPointer(),
|
|
vertexShaderBlob->GetBufferSize(),
|
|
nullptr, &m_vertexShader)))
|
|
throw DxvkError("Failed to create vertex shader");
|
|
|
|
if (FAILED(m_device->CreatePixelShader(
|
|
pixelShaderBlob->GetBufferPointer(),
|
|
pixelShaderBlob->GetBufferSize(),
|
|
nullptr, &m_pixelShader)))
|
|
throw DxvkError("Failed to create pixel shader");
|
|
|
|
std::array<D3D11_INPUT_ELEMENT_DESC, 1> vertexFormatDesc = {{
|
|
{ "IN_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(Vertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
|
}};
|
|
|
|
if (FAILED(m_device->CreateInputLayout(
|
|
vertexFormatDesc.data(),
|
|
vertexFormatDesc.size(),
|
|
vertexShaderBlob->GetBufferPointer(),
|
|
vertexShaderBlob->GetBufferSize(),
|
|
&m_vertexFormat)))
|
|
throw DxvkError("Failed to create input layout");
|
|
|
|
}
|
|
|
|
|
|
~TriangleApp() {
|
|
m_context->ClearState();
|
|
}
|
|
|
|
|
|
void run() {
|
|
this->adjustBackBuffer();
|
|
|
|
D3D11_VIEWPORT viewport;
|
|
viewport.TopLeftX = 0.0f;
|
|
viewport.TopLeftY = 0.0f;
|
|
viewport.Width = static_cast<float>(m_windowSize.w);
|
|
viewport.Height = static_cast<float>(m_windowSize.h);
|
|
viewport.MinDepth = 0.0f;
|
|
viewport.MaxDepth = 1.0f;
|
|
m_context->RSSetViewports(1, &viewport);
|
|
|
|
FLOAT color[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
|
|
m_context->OMSetRenderTargets(1, &m_bufferView, nullptr);
|
|
m_context->ClearRenderTargetView(m_bufferView.ptr(), color);
|
|
|
|
m_context->VSSetShader(m_vertexShader.ptr(), nullptr, 0);
|
|
m_context->PSSetShader(m_pixelShader.ptr(), nullptr, 0);
|
|
m_context->PSSetConstantBuffers(0, 1, &m_constantBuffer);
|
|
|
|
UINT vsStride = sizeof(Vertex);
|
|
UINT vsOffset = 0;
|
|
|
|
// Test normal draws with base vertex
|
|
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
m_context->IASetInputLayout(m_vertexFormat.ptr());
|
|
m_context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &vsStride, &vsOffset);
|
|
m_context->Draw(3, 0);
|
|
m_context->Draw(3, 3);
|
|
|
|
// Test instanced draws with base instance and base vertex
|
|
vsOffset = 6 * sizeof(Vertex);
|
|
m_context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &vsStride, &vsOffset);
|
|
m_context->DrawInstanced(3, 1, 0, 1);
|
|
m_context->DrawInstanced(3, 1, 3, 1);
|
|
|
|
// Test indexed draws with base vertex and base index
|
|
vsOffset = 12 * sizeof(Vertex);
|
|
m_context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &vsStride, &vsOffset);
|
|
m_context->IASetIndexBuffer(m_indexBuffer.ptr(), DXGI_FORMAT_R32_UINT, 0);
|
|
m_context->DrawIndexed(3, 0, 0);
|
|
m_context->DrawIndexed(3, 3, 3);
|
|
|
|
// Test default backface culling
|
|
vsOffset = 18 * sizeof(Vertex);
|
|
m_context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &vsStride, &vsOffset);
|
|
m_context->DrawIndexed(3, 6, 0);
|
|
|
|
m_context->OMSetRenderTargets(0, nullptr, nullptr);
|
|
|
|
m_swapChain->Present(0, 0);
|
|
}
|
|
|
|
|
|
void adjustBackBuffer() {
|
|
RECT windowRect = { 0, 0, 1024, 600 };
|
|
GetClientRect(m_window, &windowRect);
|
|
|
|
Extent2D newWindowSize = {
|
|
static_cast<uint32_t>(windowRect.right - windowRect.left),
|
|
static_cast<uint32_t>(windowRect.bottom - windowRect.top),
|
|
};
|
|
|
|
if (m_windowSize.w != newWindowSize.w
|
|
|| m_windowSize.h != newWindowSize.h) {
|
|
m_buffer = nullptr;
|
|
m_bufferView = nullptr;
|
|
|
|
if (FAILED(m_swapChain->ResizeBuffers(0,
|
|
newWindowSize.w, newWindowSize.h, DXGI_FORMAT_UNKNOWN, 0)))
|
|
throw DxvkError("Failed to resize back buffers");
|
|
|
|
if (FAILED(m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&m_buffer))))
|
|
throw DxvkError("Failed to get swap chain back buffer");
|
|
|
|
if (FAILED(m_device->CreateRenderTargetView(m_buffer.ptr(), nullptr, &m_bufferView)))
|
|
throw DxvkError("Failed to create render target view");
|
|
m_windowSize = newWindowSize;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
HWND m_window;
|
|
Extent2D m_windowSize = { 1024, 600 };
|
|
|
|
Com<IDXGIFactory> m_factory;
|
|
Com<IDXGIAdapter> m_adapter;
|
|
Com<ID3D11Device> m_device;
|
|
Com<ID3D11DeviceContext> m_context;
|
|
Com<IDXGISwapChain> m_swapChain;
|
|
|
|
Com<ID3D11Texture2D> m_buffer;
|
|
Com<ID3D11RenderTargetView> m_bufferView;
|
|
Com<ID3D11Buffer> m_constantBuffer;
|
|
Com<ID3D11Buffer> m_indexBuffer;
|
|
Com<ID3D11Buffer> m_vertexBuffer;
|
|
Com<ID3D11InputLayout> m_vertexFormat;
|
|
|
|
Com<ID3D11VertexShader> m_vertexShader;
|
|
Com<ID3D11PixelShader> m_pixelShader;
|
|
|
|
D3D_FEATURE_LEVEL m_featureLevel;
|
|
|
|
};
|
|
|
|
LRESULT CALLBACK WindowProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam);
|
|
|
|
int WINAPI WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow) {
|
|
HWND hWnd;
|
|
WNDCLASSEXW wc;
|
|
ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.hInstance = hInstance;
|
|
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
wc.lpszClassName = L"WindowClass1";
|
|
RegisterClassExW(&wc);
|
|
|
|
hWnd = CreateWindowExW(0,
|
|
L"WindowClass1",
|
|
L"Our First Windowed Program",
|
|
WS_OVERLAPPEDWINDOW,
|
|
300, 300,
|
|
640, 480,
|
|
nullptr,
|
|
nullptr,
|
|
hInstance,
|
|
nullptr);
|
|
ShowWindow(hWnd, nCmdShow);
|
|
|
|
MSG msg;
|
|
|
|
try {
|
|
TriangleApp app(hInstance, hWnd);
|
|
|
|
while (true) {
|
|
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
|
|
if (msg.message == WM_QUIT)
|
|
return msg.wParam;
|
|
} else {
|
|
app.run();
|
|
}
|
|
}
|
|
} catch (const dxvk::DxvkError& e) {
|
|
std::cerr << e.message() << std::endl;
|
|
return msg.wParam;
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
|
switch (message) {
|
|
case WM_CLOSE:
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|