mirror of
https://github.com/dege-diosg/dgVoodoo2
synced 2024-07-08 20:51:02 +02:00
775 lines
29 KiB
C++
775 lines
29 KiB
C++
// *****************************************************************************
|
|
// File: Presenter.cpp
|
|
//
|
|
// Description: D3D12 presentation hook implementation of dgVoodoo Addon DLL
|
|
//
|
|
// Contact person: DG
|
|
//
|
|
// *****************************************************************************
|
|
|
|
// --- Includes ----------------------------------------------------------------
|
|
|
|
#include "AddonMain.hpp"
|
|
#include "Resource.h"
|
|
|
|
// --- Defines -----------------------------------------------------------------
|
|
|
|
#define DEFAULT_GLASSIMAGEPATH "GlassImage.png"
|
|
#define DEFAULT_SEPIACOLOR 0x40F1AE72
|
|
|
|
#define VBUFFER_VERTEXCNT 256
|
|
|
|
// --- Presenter ---------------------------------------------------------------
|
|
|
|
const D3D12_BLEND_DESC Presenter::defaultBlendDesc =
|
|
{
|
|
FALSE, FALSE,
|
|
// RT0
|
|
{ FALSE, FALSE, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_LOGIC_OP_CLEAR, 0xF }
|
|
};
|
|
|
|
|
|
const D3D12_RASTERIZER_DESC Presenter::defaultRasterizerDesc =
|
|
{
|
|
D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, FALSE, 0, 0.0f, 0.0f, TRUE, FALSE, FALSE, 0, D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
|
|
};
|
|
|
|
|
|
const D3D12_DEPTH_STENCIL_DESC Presenter::defaultDepthStencilDesc =
|
|
{
|
|
FALSE, D3D12_DEPTH_WRITE_MASK_ALL, D3D12_COMPARISON_FUNC_ALWAYS, FALSE, 0xFF, 0xFF,
|
|
{ D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP},
|
|
{ D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP}
|
|
};
|
|
|
|
|
|
const D3D12_INPUT_ELEMENT_DESC Presenter::deaultInputLayout[] =
|
|
{
|
|
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
|
|
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
|
|
};
|
|
|
|
|
|
Presenter::Presenter (AddonMain& main):
|
|
main (main),
|
|
pD3D12Root (NULL),
|
|
pVSQuad (NULL),
|
|
pPSGlass (NULL),
|
|
sectionIdx (0),
|
|
pGlassFilePath (NULL),
|
|
sepiaColor (0x0)
|
|
{
|
|
memset (&glassImage, 0, sizeof (glassImage));
|
|
memset (&rootPSConstBuffer, 0, sizeof (rootPSConstBuffer));
|
|
}
|
|
|
|
|
|
Presenter::~Presenter ()
|
|
{
|
|
Exit ();
|
|
}
|
|
|
|
|
|
Presenter::AdapterData::AdapterData ():
|
|
pDevice (NULL),
|
|
pRootSignature (NULL),
|
|
pSRVDescAllocator (NULL),
|
|
pGlassTexture (NULL),
|
|
hGlassTexSRV (-1),
|
|
glassTexState (D3D12_RESOURCE_STATE_COMMON),
|
|
pPSO (NULL),
|
|
pVertexBuffer (NULL),
|
|
vbPos (0)
|
|
{
|
|
memset (&graphicsPipeline, 0, sizeof (graphicsPipeline));
|
|
}
|
|
|
|
|
|
void Presenter::ITakeLock ()
|
|
{
|
|
EnterCriticalSection (&lock);
|
|
}
|
|
|
|
|
|
void Presenter::IReleaseLock ()
|
|
{
|
|
LeaveCriticalSection (&lock);
|
|
}
|
|
|
|
|
|
// D3D12 observer functions can get called simultaneously with different adapterID's in extreme cases
|
|
// We don't want to read/write the main 'adapters' container concurrently by any chance so guard it by a lock
|
|
|
|
Presenter::AdapterData& Presenter::GetAdapterRef (UInt32 adapterID)
|
|
{
|
|
ITakeLock ();
|
|
|
|
AdapterData& adapterData = adapters[adapterID];
|
|
|
|
IReleaseLock ();
|
|
|
|
return adapterData;
|
|
}
|
|
|
|
|
|
void Presenter::EraseAdapter (UInt32 adapterID)
|
|
{
|
|
ITakeLock ();
|
|
|
|
adapters.erase (adapterID);
|
|
|
|
IReleaseLock ();
|
|
}
|
|
|
|
|
|
bool Presenter::IParsePresenterINISection ()
|
|
{
|
|
pGlassFilePath = DEFAULT_GLASSIMAGEPATH;
|
|
sepiaColor = DEFAULT_SEPIACOLOR;
|
|
|
|
bool succeeded = true;
|
|
|
|
for (UInt32 i = 0; succeeded && i < main.pINIParser->GetNumberOfProperties (sectionIdx); i++) {
|
|
const char* pName = main.pINIParser->GetPropertyName (sectionIdx, i);
|
|
|
|
if (strcmp (pName, "glassimage") == 0) {
|
|
if (main.pINIParser->GetNumberOfSubProperties (sectionIdx, i) == 0) {
|
|
if (main.pINIParser->GetNumberOfPropertyValues (sectionIdx, i) == 1) {
|
|
|
|
pGlassFilePath = main.pINIParser->GetPropertyValueAsString (sectionIdx, i, 0);
|
|
|
|
} else {
|
|
// empty value
|
|
succeeded = (main.pINIParser->GetNumberOfPropertyValues (sectionIdx, i) == 0);
|
|
}
|
|
} else {
|
|
succeeded = false;
|
|
}
|
|
|
|
} else if (strcmp (pName, "sepiacolor") == 0) {
|
|
|
|
if (main.pINIParser->GetNumberOfSubProperties (sectionIdx, i) == 0) {
|
|
if (main.pINIParser->GetNumberOfPropertyValues (sectionIdx, i) == 1) {
|
|
|
|
Int32 value = 0;
|
|
succeeded = main.pINIParser->GetPropertyValueAsInt (sectionIdx, i, 0, value);
|
|
sepiaColor = value;
|
|
|
|
} else {
|
|
// empty value
|
|
succeeded = (main.pINIParser->GetNumberOfPropertyValues (sectionIdx, i) == 0);
|
|
}
|
|
} else {
|
|
succeeded = false;
|
|
}
|
|
|
|
} else {
|
|
succeeded = false;
|
|
}
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
|
|
|
|
bool Presenter::ILoadShaders ()
|
|
{
|
|
UInt32 size = main.pAddonMainCB->RSSizeOfResource (main.hDll, MAKEINTRESOURCE (IDR_VSQUAD_5_1), L"RT_CUSTOM");
|
|
const BYTE* pData = main.pAddonMainCB->RSLoadResource (main.hDll, MAKEINTRESOURCE (IDR_VSQUAD_5_1), L"RT_CUSTOM");
|
|
if (pData != NULL) {
|
|
|
|
pVSQuad = pD3D12Root->CreateD3DBlob (size, pData);
|
|
delete[] pData;
|
|
if (pVSQuad != NULL) {
|
|
|
|
size = main.pAddonMainCB->RSSizeOfResource (main.hDll, MAKEINTRESOURCE (IDR_PSGLASS_5_1), L"RT_CUSTOM");
|
|
pData = main.pAddonMainCB->RSLoadResource (main.hDll, MAKEINTRESOURCE (IDR_PSGLASS_5_1), L"RT_CUSTOM");
|
|
|
|
if (pData != NULL) {
|
|
pPSGlass = pD3D12Root->CreateD3DBlob (size, pData);
|
|
delete[] pData;
|
|
|
|
if (pPSGlass != NULL) {
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pVSQuad->Release ();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Presenter::ICreateRootSignature (AdapterData& adapter)
|
|
{
|
|
// SRV array descriptor - just for demonstration purposes: SRV range[3..4] in space 1
|
|
const static D3D12_DESCRIPTOR_RANGE textureView =
|
|
{
|
|
D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
|
|
2, 3, 1, 0
|
|
};
|
|
|
|
const static D3D12_ROOT_PARAMETER rootEntries[] =
|
|
{
|
|
// SRV table address for texture views
|
|
D3D12RSPARAMETER (D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, 1, &textureView, D3D12_SHADER_VISIBILITY_PIXEL),
|
|
|
|
// Inline constant buffer with 4 32bit entries (RootPSConstBuffer): CBV range[0..0], space 2
|
|
D3D12RSPARAMETER (D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, 0, 2, 4, D3D12_SHADER_VISIBILITY_PIXEL)
|
|
|
|
//D3D12RSPARAMETER (D3D12_ROOT_PARAMETER_TYPE_CBV, 2U, 0U, (D3D12_SHADER_VISIBILITY) (D3D12_SHADER_VISIBILITY_ALL)),
|
|
};
|
|
|
|
// Static samplers
|
|
const static D3D12_STATIC_SAMPLER_DESC staticSamplers[] =
|
|
{
|
|
// point sampler (s0)
|
|
{
|
|
D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
|
0.0f, 1, D3D12_COMPARISON_FUNC_ALWAYS, D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, 0, D3D12_FLOAT32_MAX, 0, 0,
|
|
D3D12_SHADER_VISIBILITY_PIXEL
|
|
},
|
|
|
|
// bilinear sampler (s1)
|
|
{
|
|
D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
|
0.0f, 1, D3D12_COMPARISON_FUNC_ALWAYS, D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, 0, D3D12_FLOAT32_MAX, 1, 0,
|
|
D3D12_SHADER_VISIBILITY_PIXEL
|
|
},
|
|
};
|
|
|
|
const static D3D12_ROOT_SIGNATURE_DESC rootSignature =
|
|
{
|
|
2, rootEntries, 2, staticSamplers,
|
|
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
|
|
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS
|
|
};
|
|
|
|
adapter.pRootSignature = pD3D12Root->SerializeAndCreateRootSignature (adapter.adapterID, D3D_ROOT_SIGNATURE_VERSION_1, &rootSignature, NULL);
|
|
|
|
return adapter.pRootSignature != NULL;
|
|
}
|
|
|
|
|
|
void Presenter::IReleaseRootSignature (AdapterData& adapter)
|
|
{
|
|
// Let's tell dgVoodoo's caching infrastructure that we are about to release our
|
|
// root signature object so remove all internal entries associated with it
|
|
pD3D12Root->GPLRootSignatureReleased (adapter.adapterID, adapter.pRootSignature);
|
|
|
|
adapter.pRootSignature->Release();
|
|
adapter.pRootSignature = NULL;
|
|
}
|
|
|
|
|
|
bool Presenter::ICreateGlassTexture (AdapterData& adapter)
|
|
{
|
|
// Create a very basic 2D texture resource (1 mipmap level, format BGRA8888) in the default type heap (video memory)
|
|
// with allocated physical video memory pages (committed resource)
|
|
D3D12_HEAP_PROPERTIES heapProp =
|
|
{
|
|
D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0x0, 0x0
|
|
};
|
|
D3D12_RESOURCE_DESC desc =
|
|
{
|
|
D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, glassImage.width, glassImage.height, 1, 1,
|
|
DXGI_FORMAT_B8G8R8A8_TYPELESS, { 1, 0 }, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE
|
|
};
|
|
|
|
HRESULT hr = adapter.pDevice->CreateCommittedResource (&heapProp, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
|
|
NULL, __uuidof(ID3D12Resource), (void**) &adapter.pGlassTexture);
|
|
if (SUCCEEDED (hr)) {
|
|
|
|
// Let's allocate a shader resource view (SRV) entry for it in a CPU-only visible descriptor array through an allocator
|
|
adapter.hGlassTexSRV = adapter.pSRVDescAllocator->AllocDescriptorGroup (1);
|
|
if (adapter.hGlassTexSRV != -1) {
|
|
|
|
ID3D12GraphicsCommandListAuto* pCmdListAuto = pD3D12Root->GetCopyCommandListAuto (adapter.adapterID);
|
|
ID3D12GraphicsCommandList* pCmdList = pCmdListAuto->GetCommandListInterface ();
|
|
|
|
// Lock the command list for flush to make sure all the commands below will be written into it as a continuous block
|
|
pCmdListAuto->AFlushLock ();
|
|
|
|
// A helper method to upload data into a texture subresource - this provides basically the same functionality as UpdateSubResource in D3D11
|
|
// Give it the destination resource, the dst box, the source data user ptr, src memory layout and a memory page allocator and let it generate
|
|
// the proper commands into the cmd list; this is an upload operation so use the "upload" page allocator
|
|
D3D12_BOX dstBox = { 0, 0, 0, glassImage.width, glassImage.height, 1 };
|
|
bool succeeded = pCmdListAuto->HIUpdateSubTexture (adapter.pGlassTexture, 0, pD3D12Root->GetHeapPageAllocator (adapter.adapterID, ID3D12Root::DA_UploadBufferPageHeapAllocator),
|
|
dstBox, glassImage.pBitmap, glassImage.stride, glassImage.stride * glassImage.height);
|
|
|
|
if (succeeded) {
|
|
// The upload method leaves our texture in copy-dest state so let's transition it back to common state for
|
|
// later usage in the graphics pipeline
|
|
adapter.glassTexState = D3D12_RESOURCE_STATE_COMMON;
|
|
D3D12_RESOURCE_BARRIER barrier = { D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE };
|
|
barrier.Transition.pResource = adapter.pGlassTexture;
|
|
barrier.Transition.Subresource = 0;
|
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
|
|
pCmdList->ResourceBarrier (1, &barrier);
|
|
}
|
|
|
|
// Unlock the command list for flush and force a flush operation if the uploader method was successful (the GPU begins to work here)
|
|
pCmdListAuto->AFlushUnlock (succeeded);
|
|
|
|
if (succeeded) {
|
|
|
|
// Fill our SRV entry with a view describing a 2D texture with unorm BGRA8888 format, unswizzled components and all mipmap levels addressable
|
|
// This entry will be copied into a GPU-visible descriptor entry during generating the actual rendering commands
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc =
|
|
{
|
|
DXGI_FORMAT_B8G8R8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
|
|
};
|
|
srvDesc.Texture2D.MipLevels = -1;
|
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
|
srvDesc.Texture2D.PlaneSlice = 0;
|
|
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
|
|
adapter.pDevice->CreateShaderResourceView (adapter.pGlassTexture, &srvDesc, adapter.pSRVDescAllocator->GetCPUDescHandle (adapter.hGlassTexSRV));
|
|
|
|
return true;
|
|
}
|
|
// Release the SRV entry
|
|
adapter.pSRVDescAllocator->DeallocDescriptorGroup (adapter.hGlassTexSRV, 1);
|
|
adapter.hGlassTexSRV = 0;
|
|
}
|
|
|
|
// Release the texture - no synchronization notifications are needed here because actually nothing happened with it
|
|
adapter.pGlassTexture->Release ();
|
|
adapter.pGlassTexture = NULL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Presenter::IReleaseGlassTexture (AdapterData& adapter)
|
|
{
|
|
// Let's tell dgVoodoo's object/resource tracking system that we are about to
|
|
// release the glass texture so remove it from their entries and wait for GPU completion if needed
|
|
pD3D12Root->RTResourceDestroyed (adapter.pGlassTexture, true);
|
|
|
|
adapter.pSRVDescAllocator->DeallocDescriptorGroup (adapter.hGlassTexSRV, 1);
|
|
adapter.pGlassTexture->Release ();
|
|
adapter.pGlassTexture = NULL;
|
|
}
|
|
|
|
|
|
void Presenter::ICleanUpAdapter (AdapterData& adapter)
|
|
{
|
|
// Make sure to clear our "ownership" of the auto command lists
|
|
{
|
|
ID3D12GraphicsCommandListAuto* pCmdListAuto = pD3D12Root->GetCopyCommandListAuto (adapter.adapterID);
|
|
if (pCmdListAuto->IsCurrentId (this)) {
|
|
pCmdListAuto->ChangeId (NULL);
|
|
}
|
|
}
|
|
{
|
|
ID3D12GraphicsCommandListAuto* pCmdListAuto = pD3D12Root->GetGraphicsCommandListAuto (adapter.adapterID);
|
|
if (pCmdListAuto->IsCurrentId (this)) {
|
|
pCmdListAuto->ChangeId (NULL);
|
|
}
|
|
}
|
|
|
|
IReleaseRootSignature (adapter);
|
|
IReleaseGlassTexture (adapter);
|
|
|
|
adapter.pVertexBuffer->Release ();
|
|
}
|
|
|
|
|
|
void Presenter::IAddTransitionBarrier (ID3D12Resource* pResource, UINT stateBefore, UINT stateAfter, D3D12_RESOURCE_BARRIER* pBarriers, UInt32 numBarriers)
|
|
{
|
|
// Helper method to store a simple transition barrier for the 0th subresource of the incoming resource
|
|
|
|
pBarriers[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
pBarriers[numBarriers].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
|
|
|
pBarriers[numBarriers].Transition.pResource = pResource;
|
|
pBarriers[numBarriers].Transition.Subresource = 0;
|
|
pBarriers[numBarriers].Transition.StateBefore = (D3D12_RESOURCE_STATES) stateBefore;
|
|
pBarriers[numBarriers].Transition.StateAfter = (D3D12_RESOURCE_STATES) stateAfter;
|
|
}
|
|
|
|
|
|
// --- ID3D12RootObserver callbacks
|
|
|
|
bool Presenter::D3D12RootCreated (HMODULE /*hD3D12Dll*/, ID3D12Root* _pD3D12Root)
|
|
{
|
|
main.pAddonMainCB->IssueInfo (&main, "D3D12 root object (%p) is created.\n", _pD3D12Root);
|
|
|
|
if (ImageLoader::LoadImageA (pGlassFilePath, glassImage)) {
|
|
|
|
pD3D12Root = _pD3D12Root;
|
|
|
|
if (ILoadShaders ()) {
|
|
|
|
rootPSConstBuffer.sepiaColorR = ((sepiaColor >> 16) & 0xFF) / 255.0f;
|
|
rootPSConstBuffer.sepiaColorG = ((sepiaColor >> 8) & 0xFF) / 255.0f;
|
|
rootPSConstBuffer.sepiaColorB = ((sepiaColor >> 0) & 0xFF) / 255.0f;
|
|
rootPSConstBuffer.colorIntensity = ((sepiaColor >> 24) & 0xFF) / 255.0f;
|
|
|
|
return true;
|
|
}
|
|
|
|
delete[] glassImage.pBitmap;
|
|
memset (&glassImage, 0, sizeof (glassImage));
|
|
|
|
pD3D12Root = NULL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Presenter::D3D12RootReleased (const ID3D12Root* _pD3D12Root)
|
|
{
|
|
main.pAddonMainCB->IssueInfo (&main, "D3D12 root object (%p) is released.\n", _pD3D12Root);
|
|
|
|
delete[] glassImage.pBitmap;
|
|
memset (&glassImage, 0, sizeof (glassImage));
|
|
|
|
pD3D12Root = NULL;
|
|
}
|
|
|
|
|
|
bool Presenter::D3D12BeginUsingAdapter (UInt32 adapterID)
|
|
{
|
|
// In extreme cases D3D12 observer methods can get called concurrently with different adapter ID's
|
|
// Make sure that we won't modify concurrently the main 'adapters' container by any chance
|
|
ITakeLock ();
|
|
|
|
AdapterData data;
|
|
adapters.insert (std::make_pair (adapterID, data));
|
|
|
|
IReleaseLock ();
|
|
|
|
main.pAddonMainCB->IssueInfo (&main, "A new D3D12 adapter (%d) and its objects are initialized on root object (%p). Using ID3D12Device (%p)\n", adapterID, pD3D12Root, data.pDevice);
|
|
|
|
AdapterData& adapter = adapters[adapterID];
|
|
adapter.adapterID = adapterID;
|
|
adapter.pDevice = pD3D12Root->GetDevice (adapterID);
|
|
|
|
if (ICreateRootSignature (adapter)) {
|
|
|
|
adapter.pSRVDescAllocator = pD3D12Root->GetCBV_SRV_UAV_DescAllocator (adapterID);
|
|
|
|
if (ICreateGlassTexture (adapter)) {
|
|
|
|
adapter.vbPos = 0;
|
|
adapter.pVertexBuffer = pD3D12Root->CreateDynamicBuffer (adapterID, VBUFFER_VERTEXCNT * sizeof (Vertex), ID3D12Root::DA_VertexBufferPageHeapAllocator);
|
|
if (adapter.pVertexBuffer != NULL) {
|
|
|
|
adapter.graphicsPipeline.pRootSignature = adapter.pRootSignature;
|
|
adapter.graphicsPipeline.pVS = pVSQuad;
|
|
adapter.graphicsPipeline.pPS = pPSGlass;
|
|
adapter.graphicsPipeline.pBlendState = pD3D12Root->PLCacheGetBlend4Desc (adapterID, defaultBlendDesc);
|
|
adapter.graphicsPipeline.SampleMask = 0xFFFFFFFF;
|
|
adapter.graphicsPipeline.pRasterizerState = pD3D12Root->PLCacheGetRasterizerDesc (adapterID, defaultRasterizerDesc);
|
|
adapter.graphicsPipeline.pDepthStencilState = pD3D12Root->PLCacheGetDepthStencilDesc (adapterID, defaultDepthStencilDesc);
|
|
adapter.graphicsPipeline.pInputLayout = pD3D12Root->PLCacheGetInputLayoutDesc (adapterID, 2, deaultInputLayout);
|
|
adapter.graphicsPipeline.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED;
|
|
adapter.graphicsPipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
|
adapter.graphicsPipeline.NumRenderTargets = 1;
|
|
adapter.graphicsPipeline.DSVFormat = DXGI_FORMAT_UNKNOWN;
|
|
adapter.graphicsPipeline.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
|
|
adapter.graphicsPipeline.SampleDesc = { 1, 0 };
|
|
adapter.graphicsPipeline.NodeMask = 0;
|
|
adapter.graphicsPipeline.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
|
|
|
return true;
|
|
}
|
|
IReleaseGlassTexture (adapter);
|
|
}
|
|
IReleaseRootSignature (adapter);
|
|
}
|
|
|
|
EraseAdapter (adapterID);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Presenter::D3D12EndUsingAdapter (UInt32 adapterID)
|
|
{
|
|
ICleanUpAdapter (GetAdapterRef (adapterID));
|
|
|
|
EraseAdapter (adapterID);
|
|
|
|
main.pAddonMainCB->IssueInfo (&main, "D3D12 adapter (%d) and its objects are released on root object (%p).\n", adapterID, pD3D12Root);
|
|
}
|
|
|
|
|
|
void Presenter::D3D12SwapchainCreated (UInt32 adapterID, ID3D12Swapchain* pSwapchain, const ID3D12Root::SwapchainData& swapchainData)
|
|
{
|
|
// Just put it into our entries and don't do anything else
|
|
|
|
AdapterData& adapter = GetAdapterRef (adapterID);
|
|
|
|
SwapchainEntry entry = { pSwapchain, swapchainData };
|
|
adapter.swapchains.insert (std::make_pair (pSwapchain, entry));
|
|
}
|
|
|
|
|
|
void Presenter::D3D12SwapchainChanged (UInt32 adapterID, ID3D12Swapchain* pSwapchain, const ID3D12Root::SwapchainData& swapchainData)
|
|
{
|
|
// Just update the data in our entries and do nothing more
|
|
|
|
AdapterData& adapter = GetAdapterRef (adapterID);
|
|
|
|
SwapchainEntry entry = { pSwapchain, swapchainData };
|
|
adapter.swapchains[pSwapchain] = entry;
|
|
}
|
|
|
|
|
|
void Presenter::D3D12SwapchainReleased (UInt32 adapterID, ID3D12Swapchain* pSwapchain)
|
|
{
|
|
// Just remove it from our entries
|
|
|
|
AdapterData& adapter = GetAdapterRef (adapterID);
|
|
|
|
adapter.swapchains.erase (pSwapchain);
|
|
}
|
|
|
|
|
|
bool Presenter::D3D12SwapchainPresentBegin (UInt32 adapterID, const PresentBeginContextInput& iCtx, PresentBeginContextOutput& oCtx)
|
|
{
|
|
bool succeeded = false;
|
|
|
|
AdapterData& adapter = GetAdapterRef (adapterID);
|
|
|
|
// Get a render target - we don't do up/downscaling in this hook so we won't give back a texture factored on our own, but instead always draw into
|
|
// a proxy texure associated with the swapchain or into the swapchain directly. That's why we don't care about 'output texture expected state'
|
|
// as well here and set it to invalid -1. Expected state is what dgVoodoo transitions it into after the presentation so we know what state
|
|
// our texture is in when next time have to do sg with it.
|
|
oCtx.pOutputTexture = NULL;
|
|
oCtx.outputTexSRVCPUHandle = { NULL };
|
|
oCtx.outputTextureExpectedState = -1;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvCPUHandle = { 0 };
|
|
UINT renderTargetState = 0;
|
|
DXGI_FORMAT rtvFormat = adapter.swapchains[iCtx.pSwapchain].properties.format;
|
|
RECT dstRect = { 0, 0, 0, 0 };
|
|
{
|
|
// If dgVoodoo
|
|
// - gives us a possible direct destination texture to draw into
|
|
// - and the dimensions of the source texture rect matches the dimensions of the destination texture rect (the presentation size)
|
|
// then use that destination texture as the render target. It's a performance optimization.
|
|
|
|
// If the dimensions of the src/dst rects mismatch then it means an image scaling which we hand off to dgVoodoo by drawing into a matching size
|
|
// proxy texture instead.
|
|
|
|
if (iCtx.drawingTarget.pDstTexture != NULL &&
|
|
(iCtx.srcRect.right - iCtx.srcRect.left) == (iCtx.drawingTarget.dstRect.right - iCtx.drawingTarget.dstRect.left) &&
|
|
(iCtx.srcRect.bottom - iCtx.srcRect.top) == (iCtx.drawingTarget.dstRect.bottom - iCtx.drawingTarget.dstRect.top) ) {
|
|
|
|
oCtx.pOutputTexture = iCtx.drawingTarget.pDstTexture;
|
|
rtvCPUHandle = iCtx.drawingTarget.rtvCPUHandle;
|
|
renderTargetState = iCtx.drawingTarget.dstTextureState;
|
|
dstRect = iCtx.drawingTarget.dstRect;
|
|
|
|
} else {
|
|
|
|
// A swapchain can always have at least 2 proxy textures. We have to check out if the incoming source texture is not a proxy texture already,
|
|
// so we pick up either the 0th or the 1st of the proxy textures.
|
|
ID3D12Root::SwapchainProxyTextureData ptData;
|
|
for (UInt32 i = 0; i < 2; i++) {
|
|
pD3D12Root->GetProxyTexture (iCtx.pSwapchain, i, &ptData);
|
|
oCtx.pOutputTexture = ptData.pTexture;
|
|
oCtx.outputTexSRVCPUHandle = ptData.srvHandle;
|
|
|
|
if (ptData.pTexture != iCtx.pSrcTexture) {
|
|
// When we draw into a proxy texture then we could simply negligate the source and destination rects
|
|
// and update the full texture instead because dgVoodoo will only present the source rect anyway,
|
|
// but we can save some GPU resources this way, so this is a performance optimization
|
|
rtvCPUHandle = ptData.rtvHandle;
|
|
dstRect = iCtx.srcRect;
|
|
renderTargetState = ptData.texState;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we couldn't pick up a render target for any reason (demonstration purpose)
|
|
// then it's an error case so return with NULL output texture and let dgVoodoo ignore our hook
|
|
if (oCtx.pOutputTexture != NULL) {
|
|
|
|
|
|
ID3D12GraphicsCommandListAuto* pCmdListAuto = pD3D12Root->GetGraphicsCommandListAuto (adapterID);
|
|
|
|
// This (vertex) buffer access pattern must be familiar: if 4 new vertices still fit into it then just append them to the end of the stream or allocate a new
|
|
// physical buffer instead and zero the stream position
|
|
ID3D12Buffer::LockData lData = adapter.pVertexBuffer->Lock ((adapter.vbPos + 4) <= VBUFFER_VERTEXCNT ? ID3D12Buffer::LT_NoOverwrite : ID3D12Buffer::LT_Discard,
|
|
pCmdListAuto->AGetFence (), pCmdListAuto->GetFenceValue ());
|
|
|
|
if (lData.ptr != NULL) {
|
|
|
|
// Alloc room for 2 entries in the GPU-visible SRV descriptor ring buffer
|
|
ID3D12ResourceDescRingBuffer* pSRVRingBuffer = pD3D12Root->GetCBV_SRV_UAV_RingBuffer (adapterID);
|
|
ID3D12ResourceDescRingBuffer::AllocData rdData;
|
|
if (pSRVRingBuffer->Alloc (2, pCmdListAuto->AGetFence (), pCmdListAuto->GetFenceValue (), rdData)) {
|
|
|
|
if ((adapter.vbPos + 4) > VBUFFER_VERTEXCNT) {
|
|
adapter.vbPos = 0;
|
|
}
|
|
|
|
D3D12_RESOURCE_DESC srcDesc = iCtx.pSrcTexture->GetDesc ();
|
|
float srcWidth = (float) srcDesc.Width;
|
|
float srcHeight = (float) srcDesc.Height;
|
|
|
|
// Texture coordinates for the incoming src rect
|
|
float tuLeft = (iCtx.srcRect.left / srcWidth);
|
|
float tvTop = (iCtx.srcRect.top / srcHeight);
|
|
float tuRight = (iCtx.srcRect.right / srcWidth);
|
|
float tvBottom = (iCtx.srcRect.bottom / srcHeight);
|
|
|
|
// Make ptr volatile to avoid compiling optimizations resulting in reading video memory
|
|
// ("and dword ptr [eax], 0" and the likes)
|
|
volatile Vertex* pVertex = ((Vertex*) lData.ptr) + adapter.vbPos;
|
|
pVertex[0].pX = -1.0f; pVertex[0].pY = 1.0f; pVertex[0].tU = tuLeft; pVertex[0].tV = tvTop;
|
|
pVertex[1].pX = -1.0f; pVertex[1].pY = -1.0f; pVertex[1].tU = tuLeft; pVertex[1].tV = tvBottom;
|
|
pVertex[2].pX = 1.0f; pVertex[2].pY = 1.0f; pVertex[2].tU = tuRight; pVertex[2].tV = tvTop;
|
|
pVertex[3].pX = 1.0f; pVertex[3].pY = -1.0f; pVertex[3].tU = tuRight; pVertex[3].tV = tvBottom;
|
|
|
|
// A 2 element array containing 2 separate one-length SRV descriptor ranges; the incoming texture and our glass texture
|
|
// The SRV range allocated by the ring buffer allocator is one range with 2 elements where our 2 source descriptors will be copied to
|
|
D3D12_CPU_DESCRIPTOR_HANDLE textureSRVs[] =
|
|
{
|
|
iCtx.srvCPUHandle, adapter.pSRVDescAllocator->GetCPUDescHandle (adapter.hGlassTexSRV)
|
|
};
|
|
const static UINT textureSRVrangeSizes[] = { 1, 1 };
|
|
const static UINT srvRingBufferRangeSize = 2;
|
|
adapter.pDevice->CopyDescriptors (1, &rdData.cpuDescHandle, &srvRingBufferRangeSize, 2, textureSRVs, textureSRVrangeSizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
|
|
// The "current state" of the command list must potentially be completely updated because either somebody else has written into it or it
|
|
// got flushed since the last time we wrote into it
|
|
// (Actually, this will always be the case in this hook method; it is just a demonstration how to decide between a full/partial state update)
|
|
bool forceUpdate = pCmdListAuto->ChangeId (this);
|
|
|
|
pCmdListAuto->AFlushLock ();
|
|
ID3D12GraphicsCommandList* pCmdList = pCmdListAuto->GetCommandListInterface ();
|
|
|
|
if (forceUpdate) {
|
|
pCmdList->SetGraphicsRootSignature (adapter.pRootSignature);
|
|
}
|
|
|
|
bool updatePSO = forceUpdate;
|
|
// Get a pipeline object from the cache with the new render target format if needed
|
|
// (it's set to DXGI_FORMAT_UNKNOWN by default so it will always happen for the first time)
|
|
if (adapter.graphicsPipeline.RTVFormats[0] != rtvFormat) {
|
|
adapter.graphicsPipeline.RTVFormats[0] = rtvFormat;
|
|
|
|
adapter.pPSO = pD3D12Root->PLCacheGetGraphicsPipeline (adapterID, adapter.graphicsPipeline);
|
|
updatePSO = true;
|
|
}
|
|
|
|
if (updatePSO) {
|
|
pCmdList->SetPipelineState (adapter.pPSO);
|
|
}
|
|
|
|
// Handle resource transition barriers
|
|
{
|
|
D3D12_RESOURCE_BARRIER barriers[4];
|
|
UInt32 numBarriers = 0;
|
|
|
|
// If the glass texture is not in a pixel shader source state then let's transition it into that
|
|
if ((adapter.glassTexState & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) == 0) {
|
|
IAddTransitionBarrier (adapter.pGlassTexture, adapter.glassTexState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, barriers, numBarriers++);
|
|
adapter.glassTexState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
|
}
|
|
// If the incoming texture is not in a pixel shader source state
|
|
if ((iCtx.srcTextureState & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) == 0) {
|
|
IAddTransitionBarrier (iCtx.pSrcTexture, iCtx.srcTextureState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, barriers, numBarriers++);
|
|
}
|
|
// If the dst texture is not in rendertarget state
|
|
if ((renderTargetState & D3D12_RESOURCE_STATE_RENDER_TARGET) == 0) {
|
|
IAddTransitionBarrier (oCtx.pOutputTexture, renderTargetState, D3D12_RESOURCE_STATE_RENDER_TARGET, barriers, numBarriers++);
|
|
}
|
|
if (numBarriers != 0) {
|
|
pCmdList->ResourceBarrier (numBarriers, barriers);
|
|
}
|
|
}
|
|
|
|
if (forceUpdate) {
|
|
pCmdList->IASetPrimitiveTopology (D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
|
// Update our tiny root-inline constant buffer
|
|
pCmdList->SetGraphicsRoot32BitConstants (1, 4, &rootPSConstBuffer, 0);
|
|
}
|
|
|
|
// The things below can change independently on the cmd list state so we must always have to update them unconditionally
|
|
// The auto cmd list itself filters out a lot of redundant calls so it's not a big problem anyway
|
|
|
|
// The allocator of the vertex buffer can return a new video memory location when locking with discarding
|
|
D3D12_VERTEX_BUFFER_VIEW vbView[] =
|
|
{
|
|
lData.gpuAddress, VBUFFER_VERTEXCNT * sizeof (Vertex), sizeof (Vertex)
|
|
};
|
|
pCmdList->IASetVertexBuffers (0, 1, vbView);
|
|
|
|
// The descriptor ring buffer allocator can return an address in a physical heap other than the latest one
|
|
pCmdList->SetDescriptorHeaps (1, &rdData.pHeap);
|
|
pCmdList->SetGraphicsRootDescriptorTable (0, rdData.gpuDescHandle);
|
|
|
|
// Even the render target and the destination rects can change depending on the current state of dgVoodoo swapchain
|
|
pCmdList->OMSetRenderTargets (1, &rtvCPUHandle, TRUE, NULL);
|
|
pCmdList->RSSetScissorRects (1, &dstRect);
|
|
D3D12_VIEWPORT vp = { (FLOAT) dstRect.left, (FLOAT) dstRect.top,
|
|
(FLOAT) (dstRect.right - dstRect.left),
|
|
(FLOAT) (dstRect.bottom - dstRect.top),
|
|
0.0f, 1.0f };
|
|
pCmdList->RSSetViewports (1, &vp);
|
|
pCmdList->DrawInstanced (4, 1, adapter.vbPos, 0);
|
|
adapter.vbPos += 4;
|
|
|
|
// We do not force a flush here because it's unneeded. Commiting short command lists to execution degrades performance anyway.
|
|
// Let dgVoodoo continue writing into the command list and handle the flush scenario.
|
|
pCmdListAuto->AFlushUnlock ();
|
|
|
|
// If everything happened as expected then we can tell dgVoodoo to take the result of this hook method into account
|
|
// Otherwise, if we don't reach this point then just return false to tell the opposite
|
|
succeeded = true;
|
|
}
|
|
|
|
// Make sure to 'unlock' the vertex buffer because subsequent Lock calls in a locked state will return with unsuccesful allocation
|
|
adapter.pVertexBuffer->Unlock ();
|
|
}
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
|
|
void Presenter::D3D12SwapchainPresentEnd (UInt32 /*adapterID*/, const PresentEndContextInput& iCtx)
|
|
{
|
|
// The source image has been drawn into the swapchain and set to the expected resource state (if it was required) and that's the point
|
|
// where this callback gets called
|
|
// We get a valid non-NULL destination texture in iCtx.drawingTarget and could draw something extra into the swapchain (like some overlay data) here
|
|
// but we do not draw anything in this sample
|
|
}
|
|
|
|
|
|
bool Presenter::Init ()
|
|
{
|
|
if (IParsePresenterINISection ()) {
|
|
if (main.pAddonMainCB->RegisterForCallback (IID_D3D12RootObserver, static_cast<ID3D12RootObserver*> (this))) {
|
|
|
|
InitializeCriticalSection (&lock);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Presenter::Exit ()
|
|
{
|
|
DeleteCriticalSection (&lock);
|
|
main.pAddonMainCB->UnregisterForCallback (IID_D3D12RootObserver, static_cast<ID3D12RootObserver*> (this));
|
|
}
|
|
|
|
|