Files
renderdoc/util/test/demos/d3d12/d3d12_amd_shader_extensions.cpp
T
baldurk 856c838def Update copyright years to 2026 and fix copyright ranges
* In a previous update in 2021 many copyright ranges were truncated
  accidentally, and some files have been copy-pasted with wrong years. These
  dates have been fixed based on git history and original copyright messages.
2026-01-05 14:17:28 +00:00

512 lines
15 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "d3d12_test.h"
#include "../win32/win32_window.h"
#include "3rdparty/ags/ags_shader_intrinsics_dx12.hlsl.h"
#include "3rdparty/ags/amd_ags.h"
RD_TEST(D3D12_AMD_Shader_Extensions, D3D12GraphicsTest)
{
static constexpr const char *Description = "Tests using AMD shader extensions on D3D12.";
AGS_INITIALIZE dyn_agsInitialize = NULL;
AGS_DEINITIALIZE dyn_agsDeInitialize = NULL;
AGS_DRIVEREXTENSIONSDX12_CREATEDEVICE dyn_agsDriverExtensionsDX12_CreateDevice = NULL;
AGS_DRIVEREXTENSIONSDX12_DESTROYDEVICE dyn_agsDriverExtensionsDX12_DestroyDevice = NULL;
AGSContext *ags = NULL;
std::string BaryCentricPixel = R"EOSHADER(
float4 main() : SV_Target0
{
float2 bary = AmdExtD3DShaderIntrinsics_IjBarycentricCoords( AmdExtD3DShaderIntrinsicsBarycentric_LinearCenter );
float3 bary3 = float3(bary.x, bary.y, 1.0 - (bary.x + bary.y));
if(bary3.x > bary3.y && bary3.x > bary3.z)
return float4(1.0f, 0.0f, 0.0f, 1.0f);
else if(bary3.y > bary3.x && bary3.y > bary3.z)
return float4(0.0f, 1.0f, 0.0f, 1.0f);
else
return float4(0.0f, 0.0f, 1.0f, 1.0f);
}
)EOSHADER";
std::string MaxCompute = R"EOSHADER(
RWByteAddressBuffer inUAV : register(u1);
RWByteAddressBuffer outUAV : register(u2);
[numthreads(256, 1, 1)]
void main(uint3 threadID : SV_DispatchThreadID)
{
// read input from source
uint2 input;
input.x = inUAV.Load(threadID.x * 8);
input.y = inUAV.Load(threadID.x * 8 + 4);
AmdExtD3DShaderIntrinsics_AtomicMaxU64(outUAV, 0, input);
}
)EOSHADER";
std::string BasicPixel = R"EOSHADER(
float4 main() : SV_Target0
{
return float4(0.0f, 1.0f, 0.0f, 1.0f);
}
)EOSHADER";
std::string BasicCompute = R"EOSHADER(
RWByteAddressBuffer inUAV : register(u1);
RWByteAddressBuffer outUAV : register(u2);
[numthreads(256, 1, 1)]
void main(uint3 threadID : SV_DispatchThreadID)
{
// read input from source
uint2 input;
input.x = inUAV.Load(threadID.x * 8);
input.y = inUAV.Load(threadID.x * 8 + 4);
outUAV.Store(0, input);
}
)EOSHADER";
void Prepare(int argc, char **argv)
{
D3D12GraphicsTest::Prepare(argc, argv);
if(!Avail.empty())
return;
if(m_12On7)
{
Avail = "Can't test AGS DX12 on 12On7";
return;
}
std::string agsname = sizeof(void *) == 8 ? "amd_ags_x64.dll" : "amd_ags_x86.dll";
HMODULE agsLib = LoadLibraryA(agsname.c_str());
// try in local plugins folder
if(!agsLib)
{
agsLib = LoadLibraryA(
("../../" + std::string(sizeof(void *) == 8 ? "plugins-win64/" : "plugins/win32/") +
"amd/ags/" + agsname)
.c_str());
}
if(!agsLib)
{
// try in plugins folder next to renderdoc.dll
HMODULE rdocmod = GetModuleHandleA("renderdoc.dll");
char path[MAX_PATH + 1] = {};
if(rdocmod)
{
GetModuleFileNameA(rdocmod, path, MAX_PATH);
std::string tmp = path;
tmp.resize(tmp.size() - (sizeof("/renderdoc.dll") - 1));
agsLib = LoadLibraryA((tmp + "/plugins/amd/ags/" + agsname).c_str());
}
}
if(!agsLib)
{
Avail = "Couldn't load AGS dll";
return;
}
dyn_agsInitialize = (AGS_INITIALIZE)GetProcAddress(agsLib, "agsInitialize");
dyn_agsDeInitialize = (AGS_DEINITIALIZE)GetProcAddress(agsLib, "agsDeInitialize");
dyn_agsDriverExtensionsDX12_CreateDevice = (AGS_DRIVEREXTENSIONSDX12_CREATEDEVICE)GetProcAddress(
agsLib, "agsDriverExtensionsDX12_CreateDevice");
dyn_agsDriverExtensionsDX12_DestroyDevice =
(AGS_DRIVEREXTENSIONSDX12_DESTROYDEVICE)GetProcAddress(
agsLib, "agsDriverExtensionsDX12_DestroyDevice");
if(!dyn_agsInitialize || !dyn_agsDeInitialize || !dyn_agsDriverExtensionsDX12_CreateDevice ||
!dyn_agsDriverExtensionsDX12_DestroyDevice)
{
Avail = "AGS didn't have all necessary entry points - too old?";
return;
}
AGSReturnCode agsret = dyn_agsInitialize(
AGS_MAKE_VERSION(AMD_AGS_VERSION_MAJOR, AMD_AGS_VERSION_MINOR, AMD_AGS_VERSION_PATCH), NULL,
&ags, NULL);
if(agsret != AGS_SUCCESS || ags == NULL)
{
Avail = "AGS couldn't initialise";
return;
}
ID3D12Device *ags_devHandle = NULL;
CreateExtendedDevice(&ags_devHandle, NULL);
// once we've checked that we can create an extension device on an adapter, we can release it
// and return ready to run
if(ags_devHandle)
{
Avail = "";
unsigned int dummy = 0;
dyn_agsDriverExtensionsDX12_DestroyDevice(ags, ags_devHandle, &dummy);
return;
}
Avail = "AGS couldn't create device on any selected adapter.";
}
void CreateExtendedDevice(ID3D12Device * *ags_devHandle,
AGSDX12ReturnedParams::ExtensionsSupported * exts)
{
std::vector<IDXGIAdapterPtr> adapters = GetAdapters();
for(IDXGIAdapterPtr a : adapters)
{
AGSDX12DeviceCreationParams devCreate = {};
AGSDX12ExtensionParams extCreate = {};
AGSDX12ReturnedParams ret = {};
devCreate.FeatureLevel = minFeatureLevel;
devCreate.iid = __uuidof(ID3D12Device);
devCreate.pAdapter = a.GetInterfacePtr();
extCreate.pAppName = L"RenderDoc demos";
extCreate.pEngineName = L"RenderDoc demos";
AGSReturnCode agsret =
dyn_agsDriverExtensionsDX12_CreateDevice(ags, &devCreate, &extCreate, &ret);
if(agsret == AGS_SUCCESS && ret.pDevice)
{
// don't accept devices that don't support the intrinsics we want
if(!ret.extensionsSupported.intrinsics16 || !ret.extensionsSupported.intrinsics19)
{
unsigned int dummy = 0;
dyn_agsDriverExtensionsDX12_DestroyDevice(ags, ret.pDevice, &dummy);
}
else
{
*ags_devHandle = ret.pDevice;
if(exts)
*exts = ret.extensionsSupported;
return;
}
}
}
}
int main()
{
// initialise, create window, create device, etc
if(!Init())
return 3;
// release the old device and swapchain
dev = NULL;
d3d12Debug = NULL;
queue = NULL;
m_GPUSyncFence = NULL;
m_Alloc = NULL;
m_DebugList = NULL;
m_RTV = NULL;
m_DSV = NULL;
m_Sampler = NULL;
m_CBVUAVSRV = NULL;
m_Clear = NULL;
m_ReadbackBuffer = NULL;
m_UploadBuffer = NULL;
swapBlitSig = NULL;
swapBlitPso = NULL;
infoqueue = NULL;
dev1 = NULL;
dev2 = NULL;
dev3 = NULL;
dev4 = NULL;
dev5 = NULL;
dev6 = NULL;
dev7 = NULL;
dev8 = NULL;
// and swapchain & related
swap = NULL;
bbTex[0] = bbTex[1] = NULL;
// we don't use these directly, just copy them into the real device so we know that ags is the
// one to destroy the last reference to the device
ID3D12Device *ags_devHandle = NULL;
AGSDX12ReturnedParams::ExtensionsSupported features = {};
CreateExtendedDevice(&ags_devHandle, &features);
dev = ags_devHandle;
if(!features.UAVBindSlot || !features.intrinsics16 || !features.intrinsics19)
{
dev = NULL;
TEST_ERROR("Couldn't create AMD device with required features");
return 4;
}
// recreate things we need on the new device
PostDeviceCreate();
// create the swapchain on the new AGS-extended device
DXGI_SWAP_CHAIN_DESC1 swapDesc = MakeSwapchainDesc();
{
IDXGIFactory4Ptr factory4 = m_Factory;
CHECK_HR(factory4->CreateSwapChainForHwnd(queue, ((Win32Window *)mainWindow)->wnd, &swapDesc,
NULL, NULL, &swap));
CHECK_HR(swap->GetBuffer(0, __uuidof(ID3D12Resource), (void **)&bbTex[0]));
CHECK_HR(swap->GetBuffer(1, __uuidof(ID3D12Resource), (void **)&bbTex[1]));
}
ID3D12RootSignaturePtr sig = MakeSig({
tableParam(D3D12_SHADER_VISIBILITY_ALL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 0, 3, 0),
tableParam(D3D12_SHADER_VISIBILITY_ALL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
AGS_DX12_SHADER_INSTRINSICS_SPACE_ID, 0, 1, 3),
});
std::string ags_header = ags_shader_intrinsics_dx12_hlsl();
const std::string profilesuffix[2] = {"_5_1", "_6_0"};
const std::wstring namesuffix[2] = {L"SM51", L"SM60"};
ID3DBlobPtr vsblob[2];
ID3DBlobPtr psblob[2];
ID3DBlobPtr csblob[2];
bool valid[2] = {};
ID3D12PipelineStatePtr pso[2];
ID3D12PipelineStatePtr cso[2];
for(int i = 0; i < ARRAY_COUNT(profilesuffix); i++)
{
vsblob[i] = Compile(D3DDefaultVertex, "main", "vs" + profilesuffix[i]);
// if we don't have DXIL support we can't compile anything, even a dummy shader
if(i == 2 && !m_DXILSupport)
continue;
valid[i] = true;
// can't skip optimising and still have the extensions work, sadly
psblob[i] = Compile(ags_header + BaryCentricPixel, "main", "ps" + profilesuffix[i],
CompileOptionFlags::None);
csblob[i] = Compile(ags_header + MaxCompute, "main", "cs" + profilesuffix[i],
CompileOptionFlags::None);
pso[i] = MakePSO().RootSig(sig).InputLayout().VS(vsblob[i]).PS(psblob[i]);
cso[i] = MakePSO().RootSig(sig).CS(csblob[i]);
pso[i]->SetName((L"pipe" + namesuffix[i]).c_str());
cso[i]->SetName((L"cspipe" + namesuffix[i]).c_str());
}
ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri);
const int numInputValues = 16384;
std::vector<uint64_t> values;
values.resize(numInputValues);
uint64_t cpuMax = 0;
for(uint64_t &v : values)
{
v = 0;
for(uint32_t byte = 0; byte < 8; byte++)
{
uint64_t b = (rand() & 0xff0) >> 4;
v |= b << (byte * 8);
}
cpuMax = std::max(v, cpuMax);
}
ID3D12ResourcePtr inBuf =
MakeBuffer().UAV().Data(values.data()).Size(UINT(sizeof(uint64_t) * values.size()));
ID3D12ResourcePtr outBuf = MakeBuffer().UAV().Size(32);
ResourceBarrier(inBuf, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
ResourceBarrier(outBuf, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
outBuf->SetName(L"outBuf");
D3D12_GPU_DESCRIPTOR_HANDLE uav0gpu =
MakeUAV(inBuf).Format(DXGI_FORMAT_R32_TYPELESS).ByteAddressed().CreateGPU(1);
D3D12_GPU_DESCRIPTOR_HANDLE uav1gpu =
MakeUAV(outBuf).Format(DXGI_FORMAT_R32_TYPELESS).ByteAddressed().CreateGPU(2);
D3D12_CPU_DESCRIPTOR_HANDLE uav0cpu =
MakeUAV(inBuf).Format(DXGI_FORMAT_R32_TYPELESS).ByteAddressed().CreateClearCPU(1);
D3D12_CPU_DESCRIPTOR_HANDLE uav1cpu =
MakeUAV(outBuf).Format(DXGI_FORMAT_R32_TYPELESS).ByteAddressed().CreateClearCPU(2);
while(Running())
{
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
Reset(cmd);
ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D12_CPU_DESCRIPTOR_HANDLE rtv =
MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0);
ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f});
// force inclusion of all pipelines
for(int i = 0; i < ARRAY_COUNT(pso); i++)
{
if(pso[i])
cmd->SetPipelineState(pso[i]);
if(cso[i])
cmd->SetPipelineState(cso[i]);
}
cmd->Close();
Submit({cmd});
float x = (float)screenWidth / 2.0f;
float y = (float)screenHeight / 2.0f;
const std::string passname[] = {"SM50", "SM51", "SM60"};
for(int i = 0; i < ARRAY_COUNT(pso); i++)
{
if(!valid[i])
continue;
cmd = GetCommandBuffer();
Reset(cmd);
pushMarker(cmd, passname[i]);
UINT zero[4] = {};
cmd->ClearUnorderedAccessViewUint(uav1gpu, uav1cpu, outBuf, zero, 0, NULL);
OMSetRenderTargets(cmd, {rtv}, {});
IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0);
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr());
RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight});
RSSetViewport(cmd, {x * float(i % 2), y * float(i / 2), x, y, 0.0f, 1.0f});
setMarker(cmd, passname[i] + " Draw");
cmd->SetPipelineState(pso[i]);
cmd->SetGraphicsRootSignature(sig);
cmd->DrawInstanced(3, 1, 0, 0);
cmd->SetPipelineState(cso[i]);
cmd->SetComputeRootSignature(sig);
cmd->SetComputeRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart());
setMarker(cmd, passname[i] + " Dispatch");
cmd->Dispatch(numInputValues / 256, 1, 1);
cmd->Close();
Submit({cmd});
GPUSync();
std::vector<byte> output = GetBufferData(outBuf, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0, 8);
cmd = GetCommandBuffer();
Reset(cmd);
uint64_t gpuMax = 0;
memcpy(&gpuMax, output.data(), sizeof(uint64_t));
setMarker(cmd, passname[i] + " cpuMax: " + std::to_string(cpuMax));
setMarker(cmd, passname[i] + " gpuMax: " + std::to_string(gpuMax));
popMarker(cmd);
cmd->Close();
Submit({cmd});
}
cmd = GetCommandBuffer();
Reset(cmd);
FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmd->Close();
Submit({cmd});
Present();
}
dev = NULL;
unsigned int dummy = 0;
dyn_agsDriverExtensionsDX12_DestroyDevice(ags, ags_devHandle, &dummy);
dyn_agsDeInitialize(ags);
return 0;
}
};
REGISTER_TEST();