704 lines
26 KiB
C++
704 lines
26 KiB
C++
// Copyright (C) 2021-2022, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
|
|
#include "pch.h"
|
|
|
|
using namespace verus;
|
|
using namespace verus::CGI;
|
|
|
|
TextureD3D12::TextureD3D12()
|
|
{
|
|
}
|
|
|
|
TextureD3D12::~TextureD3D12()
|
|
{
|
|
Done();
|
|
}
|
|
|
|
void TextureD3D12::Init(RcTextureDesc desc)
|
|
{
|
|
VERUS_INIT();
|
|
VERUS_RT_ASSERT(desc._width > 0 && desc._height > 0);
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_RENDERER_D3D12;
|
|
HRESULT hr = 0;
|
|
|
|
// Variables:
|
|
_size = Vector4(
|
|
float(desc._width),
|
|
float(desc._height),
|
|
1.f / desc._width,
|
|
1.f / desc._height);
|
|
if (desc._name)
|
|
_name = desc._name;
|
|
_desc = desc;
|
|
_initAtFrame = renderer.GetFrameCount();
|
|
if (desc._flags & TextureDesc::Flags::anyShaderResource)
|
|
_mainLayout = ImageLayout::xsReadOnly;
|
|
_bytesPerPixel = FormatToBytesPerPixel(desc._format);
|
|
|
|
_desc._mipLevels = _desc._mipLevels ? _desc._mipLevels : Math::ComputeMipLevels(_desc._width, _desc._height, _desc._depth);
|
|
const bool renderTarget = (_desc._flags & TextureDesc::Flags::colorAttachment);
|
|
const bool depthFormat = IsDepthFormat(_desc._format);
|
|
const bool depthSampled = _desc._flags & (TextureDesc::Flags::depthSampledR | TextureDesc::Flags::depthSampledW);
|
|
const bool cubeMap = (_desc._flags & TextureDesc::Flags::cubeMap);
|
|
if (cubeMap)
|
|
_desc._arrayLayers *= +CubeMapFace::count;
|
|
|
|
// Create:
|
|
D3D12_RESOURCE_DESC resDesc = {};
|
|
resDesc.Dimension = (_desc._depth > 1) ? D3D12_RESOURCE_DIMENSION_TEXTURE3D : D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
resDesc.Width = _desc._width;
|
|
resDesc.Height = _desc._height;
|
|
resDesc.DepthOrArraySize = Math::Max(_desc._depth, _desc._arrayLayers);
|
|
resDesc.MipLevels = _desc._mipLevels;
|
|
resDesc.Format = ToNativeFormat(_desc._format, depthSampled);
|
|
resDesc.SampleDesc.Count = _desc._sampleCount;
|
|
resDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
|
if (renderTarget)
|
|
resDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
|
if (depthFormat)
|
|
{
|
|
resDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
|
if (!depthSampled)
|
|
resDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
|
|
}
|
|
D3D12_RESOURCE_STATES initialResourceState = ToNativeImageLayout(_mainLayout);
|
|
D3D12_CLEAR_VALUE clearValue = {};
|
|
clearValue.Format = ToNativeFormat(_desc._format, false);
|
|
memcpy(clearValue.Color, _desc._clearValue.ToPointer(), sizeof(clearValue.Color));
|
|
if (depthFormat)
|
|
{
|
|
initialResourceState = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
|
clearValue.DepthStencil.Depth = _desc._clearValue.getX();
|
|
clearValue.DepthStencil.Stencil = static_cast<UINT8>(_desc._clearValue.getY());
|
|
if (_desc._flags & TextureDesc::Flags::depthSampledR)
|
|
initialResourceState = D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
|
}
|
|
D3D12MA::ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
|
|
if (FAILED(hr = pRendererD3D12->GetMaAllocator()->CreateResource(
|
|
&allocDesc,
|
|
&resDesc,
|
|
initialResourceState,
|
|
(renderTarget || depthFormat) ? &clearValue : nullptr,
|
|
&_resource._pMaAllocation,
|
|
IID_PPV_ARGS(&_resource._pResource))))
|
|
throw VERUS_RUNTIME_ERROR << "CreateResource(D3D12_HEAP_TYPE_DEFAULT); hr=" << VERUS_HR(hr);
|
|
_resource._pResource->SetName(_C(Str::Utf8ToWide(_name)));
|
|
|
|
// Optional mipmap:
|
|
if (_desc._flags & TextureDesc::Flags::generateMips)
|
|
{
|
|
VERUS_RT_ASSERT(_desc._mipLevels > 1);
|
|
// Create storage image for compute shader. First mip level is not required.
|
|
const int uaMipLevels = Math::Max(1, _desc._mipLevels - 1);
|
|
D3D12_RESOURCE_DESC uaResDesc = resDesc;
|
|
uaResDesc.Width = Math::Max(1, _desc._width >> 1);
|
|
uaResDesc.Height = Math::Max(1, _desc._height >> 1);
|
|
uaResDesc.MipLevels = uaMipLevels;
|
|
uaResDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
// sRGB cannot be used with UAV:
|
|
switch (uaResDesc.Format)
|
|
{
|
|
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: uaResDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
|
|
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: uaResDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; break;
|
|
}
|
|
|
|
D3D12MA::ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
|
|
if (FAILED(hr = pRendererD3D12->GetMaAllocator()->CreateResource(
|
|
&allocDesc,
|
|
&uaResDesc,
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
|
nullptr,
|
|
&_uaResource._pMaAllocation,
|
|
IID_PPV_ARGS(&_uaResource._pResource))))
|
|
throw VERUS_RUNTIME_ERROR << "CreateResource(D3D12_HEAP_TYPE_DEFAULT); hr=" << VERUS_HR(hr);
|
|
_uaResource._pResource->SetName(_C(Str::Utf8ToWide(_name + " (UAV)")));
|
|
|
|
_vCshGenerateMips.reserve(Math::DivideByMultiple<int>(_desc._mipLevels, 4));
|
|
_dhUAV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, uaMipLevels * _desc._arrayLayers);
|
|
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
|
uavDesc.Format = uaResDesc.Format;
|
|
uavDesc.ViewDimension = (_desc._arrayLayers > 1) ? D3D12_UAV_DIMENSION_TEXTURE2DARRAY : D3D12_UAV_DIMENSION_TEXTURE2D;
|
|
VERUS_FOR(layer, _desc._arrayLayers)
|
|
{
|
|
VERUS_FOR(mip, uaMipLevels)
|
|
{
|
|
if (D3D12_UAV_DIMENSION_TEXTURE2D == uavDesc.ViewDimension)
|
|
{
|
|
uavDesc.Texture2D.MipSlice = mip;
|
|
pRendererD3D12->GetD3DDevice()->CreateUnorderedAccessView(_uaResource._pResource.Get(), nullptr, &uavDesc, _dhUAV.AtCPU(mip));
|
|
}
|
|
else
|
|
{
|
|
uavDesc.Texture2DArray.MipSlice = mip;
|
|
uavDesc.Texture2DArray.FirstArraySlice = layer;
|
|
uavDesc.Texture2DArray.ArraySize = 1;
|
|
if (cubeMap)
|
|
{
|
|
const int cubeIndex = layer / +CubeMapFace::count;
|
|
const int faceIndex = layer % +CubeMapFace::count;
|
|
uavDesc.Texture2DArray.FirstArraySlice = (cubeIndex * +CubeMapFace::count) + ToNativeCubeMapFace(static_cast<CubeMapFace>(faceIndex));
|
|
}
|
|
pRendererD3D12->GetD3DDevice()->CreateUnorderedAccessView(_uaResource._pResource.Get(), nullptr, &uavDesc, _dhUAV.AtCPU(mip + layer * uaMipLevels));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Optional readback buffer:
|
|
if (_desc._readbackMip != SHRT_MAX)
|
|
{
|
|
if (_desc._readbackMip < 0)
|
|
_desc._readbackMip = _desc._mipLevels + _desc._readbackMip;
|
|
const int w = Math::Max(1, _desc._width >> _desc._readbackMip);
|
|
const int h = Math::Max(1, _desc._height >> _desc._readbackMip);
|
|
const UINT64 bufferSize = _bytesPerPixel * w * h;
|
|
_vReadbackBuffers.resize(BaseRenderer::s_ringBufferSize);
|
|
for (auto& readbackBuffer : _vReadbackBuffers)
|
|
{
|
|
D3D12MA::ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.HeapType = D3D12_HEAP_TYPE_READBACK;
|
|
const auto resDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
|
|
if (FAILED(hr = pRendererD3D12->GetMaAllocator()->CreateResource(
|
|
&allocDesc,
|
|
&resDesc,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
nullptr,
|
|
&readbackBuffer._pMaAllocation,
|
|
IID_PPV_ARGS(&readbackBuffer._pResource))))
|
|
throw VERUS_RUNTIME_ERROR << "CreateResource(D3D12_HEAP_TYPE_READBACK); hr=" << VERUS_HR(hr);
|
|
readbackBuffer._pResource->SetName(_C(Str::Utf8ToWide(_name + " (Readback)")));
|
|
}
|
|
}
|
|
|
|
// Create views:
|
|
if (renderTarget)
|
|
{
|
|
if (cubeMap)
|
|
{
|
|
_dhRTV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, +CubeMapFace::count);
|
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
|
rtvDesc.Format = ToNativeFormat(_desc._format, false);
|
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
{
|
|
rtvDesc.Texture2DArray.FirstArraySlice = ToNativeCubeMapFace(static_cast<CubeMapFace>(i));
|
|
rtvDesc.Texture2DArray.ArraySize = 1;
|
|
pRendererD3D12->GetD3DDevice()->CreateRenderTargetView(_resource._pResource.Get(), &rtvDesc, _dhRTV.AtCPU(i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_dhRTV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateRenderTargetView(_resource._pResource.Get(), nullptr, _dhRTV.AtCPU(0));
|
|
}
|
|
}
|
|
if (depthFormat)
|
|
{
|
|
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
|
|
dsvDesc.Format = clearValue.Format;
|
|
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
|
_dhDSV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateDepthStencilView(_resource._pResource.Get(), &dsvDesc, _dhDSV.AtCPU(0));
|
|
if (depthSampled)
|
|
{
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
|
srvDesc.Format = ToNativeSampledDepthFormat(_desc._format);
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
srvDesc.Texture2D.MipLevels = 1;
|
|
_dhSRV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateShaderResourceView(_resource._pResource.Get(), &srvDesc, _dhSRV.AtCPU(0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cubeMap)
|
|
{
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
|
srvDesc.Format = ToNativeFormat(_desc._format, false);
|
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
if (_desc._arrayLayers > +CubeMapFace::count)
|
|
{
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
|
srvDesc.TextureCubeArray.MipLevels = _desc._mipLevels;
|
|
srvDesc.TextureCubeArray.NumCubes = _desc._arrayLayers / +CubeMapFace::count;
|
|
}
|
|
else
|
|
{
|
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
|
srvDesc.TextureCube.MipLevels = _desc._mipLevels;
|
|
}
|
|
_dhSRV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateShaderResourceView(_resource._pResource.Get(), &srvDesc, _dhSRV.AtCPU(0));
|
|
}
|
|
else
|
|
{
|
|
_dhSRV.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateShaderResourceView(_resource._pResource.Get(), nullptr, _dhSRV.AtCPU(0));
|
|
}
|
|
}
|
|
|
|
// Custom sampler:
|
|
if (_desc._pSamplerDesc)
|
|
CreateSampler();
|
|
|
|
_vStagingBuffers.reserve(_desc._mipLevels * _desc._arrayLayers);
|
|
}
|
|
|
|
void TextureD3D12::Done()
|
|
{
|
|
ForceScheduled();
|
|
|
|
_dhSampler.Reset();
|
|
_dhDSV.Reset();
|
|
_dhRTV.Reset();
|
|
_dhUAV.Reset();
|
|
_dhSRV.Reset();
|
|
|
|
_vCshGenerateMips.clear();
|
|
|
|
for (auto& x : _vReadbackBuffers)
|
|
{
|
|
VERUS_SMART_RELEASE(x._pMaAllocation);
|
|
VERUS_COM_RELEASE_CHECK(x._pResource.Get());
|
|
x._pResource.Reset();
|
|
}
|
|
_vReadbackBuffers.clear();
|
|
|
|
VERUS_SMART_RELEASE(_uaResource._pMaAllocation);
|
|
VERUS_COM_RELEASE_CHECK(_uaResource._pResource.Get());
|
|
_uaResource._pResource.Reset();
|
|
VERUS_SMART_RELEASE(_resource._pMaAllocation);
|
|
VERUS_COM_RELEASE_CHECK(_resource._pResource.Get());
|
|
_resource._pResource.Reset();
|
|
|
|
VERUS_DONE(TextureD3D12);
|
|
}
|
|
|
|
void TextureD3D12::UpdateSubresource(const void* p, int mipLevel, int arrayLayer, PBaseCommandBuffer pCB)
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_RENDERER_D3D12;
|
|
HRESULT hr = 0;
|
|
|
|
const int w = Math::Max(1, _desc._width >> mipLevel);
|
|
const int h = Math::Max(1, _desc._height >> mipLevel);
|
|
UINT64 bufferSize = Math::AlignUp(_bytesPerPixel * w, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * h;
|
|
if (IsBC(_desc._format))
|
|
bufferSize = Math::AlignUp(IO::DDSHeader::ComputeBcPitch(w, h, Is4BitsBC(_desc._format)), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * Math::Max(h / 4, 1);
|
|
|
|
const int sbIndex = arrayLayer * _desc._mipLevels + mipLevel;
|
|
if (_vStagingBuffers.size() <= sbIndex)
|
|
_vStagingBuffers.resize(sbIndex + 1);
|
|
|
|
auto& sb = _vStagingBuffers[sbIndex];
|
|
if (!sb._pResource)
|
|
{
|
|
D3D12MA::ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
|
|
const auto resDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
|
|
if (FAILED(hr = pRendererD3D12->GetMaAllocator()->CreateResource(
|
|
&allocDesc,
|
|
&resDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
nullptr,
|
|
&sb._pMaAllocation,
|
|
IID_PPV_ARGS(&sb._pResource))))
|
|
throw VERUS_RUNTIME_ERROR << "CreateResource(D3D12_HEAP_TYPE_UPLOAD); hr=" << VERUS_HR(hr);
|
|
sb._pResource->SetName(_C(Str::Utf8ToWide(_name + " (Staging)")));
|
|
}
|
|
|
|
if (!pCB)
|
|
pCB = renderer.GetCommandBuffer().Get();
|
|
auto pCmdList = static_cast<PCommandBufferD3D12>(pCB)->GetD3DGraphicsCommandList();
|
|
pCB->PipelineImageMemoryBarrier(TexturePtr::From(this), _mainLayout, ImageLayout::transferDst, mipLevel, arrayLayer);
|
|
D3D12_SUBRESOURCE_DATA sd = {};
|
|
sd.pData = p;
|
|
if (IsBC(_desc._format))
|
|
{
|
|
sd.RowPitch = IO::DDSHeader::ComputeBcPitch(w, h, Is4BitsBC(_desc._format));
|
|
sd.SlicePitch = IO::DDSHeader::ComputeBcLevelSize(w, h, Is4BitsBC(_desc._format));
|
|
}
|
|
else
|
|
{
|
|
sd.RowPitch = _bytesPerPixel * w;
|
|
sd.SlicePitch = _bytesPerPixel * w * h;
|
|
}
|
|
const UINT subresource = D3D12CalcSubresource(mipLevel, arrayLayer, 0, _desc._mipLevels, _desc._arrayLayers);
|
|
UpdateSubresources<1>(
|
|
pCmdList,
|
|
_resource._pResource.Get(),
|
|
sb._pResource.Get(), 0,
|
|
subresource, 1, &sd);
|
|
pCB->PipelineImageMemoryBarrier(TexturePtr::From(this), ImageLayout::transferDst, _mainLayout, mipLevel, arrayLayer);
|
|
|
|
Schedule();
|
|
}
|
|
|
|
bool TextureD3D12::ReadbackSubresource(void* p, bool recordCopyCommand, PBaseCommandBuffer pCB)
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
HRESULT hr = 0;
|
|
|
|
const int w = Math::Max(1, _desc._width >> _desc._readbackMip);
|
|
const int h = Math::Max(1, _desc._height >> _desc._readbackMip);
|
|
const UINT rowPitch = Math::AlignUp(_bytesPerPixel * w, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
|
|
|
auto& rb = _vReadbackBuffers[renderer->GetRingBufferIndex()];
|
|
|
|
if (recordCopyCommand)
|
|
{
|
|
if (!pCB)
|
|
pCB = renderer.GetCommandBuffer().Get();
|
|
auto pCmdList = static_cast<PCommandBufferD3D12>(pCB)->GetD3DGraphicsCommandList();
|
|
pCB->PipelineImageMemoryBarrier(TexturePtr::From(this), _mainLayout, ImageLayout::transferSrc, _desc._readbackMip, 0);
|
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
|
|
footprint.Footprint.Format = ToNativeFormat(_desc._format, false);
|
|
footprint.Footprint.Width = w;
|
|
footprint.Footprint.Height = h;
|
|
footprint.Footprint.Depth = 1;
|
|
footprint.Footprint.RowPitch = rowPitch;
|
|
const UINT subresource = D3D12CalcSubresource(_desc._readbackMip, 0, 0, _desc._mipLevels, _desc._arrayLayers);
|
|
const auto dstCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(rb._pResource.Get(), footprint);
|
|
const auto srcCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(_resource._pResource.Get(), subresource);
|
|
pCmdList->CopyTextureRegion(
|
|
&dstCopyLoc,
|
|
0, 0, 0,
|
|
&srcCopyLoc,
|
|
nullptr);
|
|
pCB->PipelineImageMemoryBarrier(TexturePtr::From(this), ImageLayout::transferSrc, _mainLayout, _desc._readbackMip, 0);
|
|
}
|
|
|
|
if (p) // Read current data from readback buffer:
|
|
{
|
|
CD3DX12_RANGE readRange(0, 0);
|
|
void* pData = nullptr;
|
|
if (FAILED(hr = rb._pResource->Map(0, &readRange, &pData)))
|
|
throw VERUS_RUNTIME_ERROR << "Map(); hr=" << VERUS_HR(hr);
|
|
BYTE* pDst = static_cast<BYTE*>(p);
|
|
BYTE* pSrc = static_cast<BYTE*>(pData);
|
|
VERUS_FOR(i, h)
|
|
{
|
|
memcpy(
|
|
pDst + i * _bytesPerPixel * w,
|
|
pSrc + i * rowPitch,
|
|
_bytesPerPixel * w);
|
|
}
|
|
rb._pResource->Unmap(0, nullptr);
|
|
}
|
|
|
|
return _initAtFrame + BaseRenderer::s_ringBufferSize < renderer.GetFrameCount();
|
|
}
|
|
|
|
void TextureD3D12::GenerateMips(PBaseCommandBuffer pCB)
|
|
{
|
|
VERUS_RT_ASSERT(_desc._flags & TextureDesc::Flags::generateMips);
|
|
if (_desc._flags & TextureDesc::Flags::cubeMap)
|
|
return GenerateCubeMapMips(pCB);
|
|
|
|
VERUS_QREF_RENDERER;
|
|
|
|
if (!pCB)
|
|
pCB = renderer.GetCommandBuffer().Get();
|
|
auto pCmdList = static_cast<PCommandBufferD3D12>(pCB)->GetD3DGraphicsCommandList();
|
|
auto shader = renderer.GetShaderGenerateMips();
|
|
auto& ub = renderer.GetUbGenerateMips();
|
|
auto tex = TexturePtr::From(this);
|
|
|
|
const int maxMipLevel = _desc._mipLevels - 1;
|
|
|
|
// Transition state for sampling in compute shader:
|
|
if (_mainLayout != ImageLayout::xsReadOnly)
|
|
pCB->PipelineImageMemoryBarrier(tex, _mainLayout, ImageLayout::xsReadOnly, Range(0, _desc._mipLevels));
|
|
|
|
pCB->BindPipeline(renderer.GetPipelineGenerateMips(!!(_desc._flags & TextureDesc::Flags::exposureMips)));
|
|
|
|
shader->BeginBindDescriptors();
|
|
int dispatchIndex = 0;
|
|
for (int srcMip = 0; srcMip < maxMipLevel;)
|
|
{
|
|
const int srcWidth = Math::Max(1, _desc._width >> srcMip);
|
|
const int srcHeight = Math::Max(1, _desc._height >> srcMip);
|
|
const int dstWidth = Math::Max(1, srcWidth >> 1);
|
|
const int dstHeight = Math::Max(1, srcHeight >> 1);
|
|
|
|
int dispatchMipCount = Math::LowestBit((dstWidth == 1 ? dstHeight : dstWidth) | (dstHeight == 1 ? dstWidth : dstHeight));
|
|
dispatchMipCount = Math::Min(4, dispatchMipCount + 1);
|
|
dispatchMipCount = ((srcMip + dispatchMipCount) >= _desc._mipLevels) ? _desc._mipLevels - srcMip - 1 : dispatchMipCount; // Edge case.
|
|
|
|
ub._srcMipLevel = srcMip;
|
|
ub._mipLevelCount = dispatchMipCount;
|
|
ub._srcDimensionCase = ((srcHeight & 1) << 1) | (srcWidth & 1);
|
|
ub._srgb = IsSRGBFormat(_desc._format);
|
|
ub._dstTexelSize.x = 1.f / dstWidth;
|
|
ub._dstTexelSize.y = 1.f / dstHeight;
|
|
|
|
int mipLevels[5] = {}; // For input texture (always mip 0) and 4 UAV mips.
|
|
VERUS_FOR(i, dispatchMipCount)
|
|
mipLevels[i + 1] = srcMip + i;
|
|
const CSHandle complexSetHandle = shader->BindDescriptorSetTextures(0 | ShaderD3D12::SET_MOD_TO_VIEW_HEAP, { tex, tex, tex, tex, tex }, mipLevels);
|
|
_vCshGenerateMips.push_back(complexSetHandle);
|
|
pCB->BindDescriptors(shader, 0, complexSetHandle);
|
|
|
|
pCB->Dispatch(Math::DivideByMultiple(dstWidth, 8), Math::DivideByMultiple(dstHeight, 8));
|
|
|
|
auto rb = CD3DX12_RESOURCE_BARRIER::UAV(_uaResource._pResource.Get());
|
|
pCmdList->ResourceBarrier(1, &rb);
|
|
|
|
// Transition state for upcoming CopyTextureRegion():
|
|
{
|
|
CD3DX12_RESOURCE_BARRIER barriers[4];
|
|
int barrierCount = 0;
|
|
VERUS_FOR(i, dispatchMipCount)
|
|
{
|
|
const int subUAV = srcMip + i;
|
|
barriers[barrierCount++] = CD3DX12_RESOURCE_BARRIER::Transition(_uaResource._pResource.Get(),
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE, subUAV);
|
|
}
|
|
pCmdList->ResourceBarrier(barrierCount, barriers);
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::xsReadOnly, ImageLayout::transferDst, Range(0, dispatchMipCount).OffsetBy(srcMip + 1));
|
|
}
|
|
VERUS_FOR(i, dispatchMipCount)
|
|
{
|
|
const int subUAV = srcMip + i;
|
|
const int subSRV = subUAV + 1;
|
|
const auto dstCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(_resource._pResource.Get(), subSRV);
|
|
const auto srcCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(_uaResource._pResource.Get(), subUAV);
|
|
pCmdList->CopyTextureRegion(
|
|
&dstCopyLoc,
|
|
0, 0, 0,
|
|
&srcCopyLoc,
|
|
nullptr);
|
|
}
|
|
// Transition state for next Dispatch():
|
|
{
|
|
CD3DX12_RESOURCE_BARRIER barriers[4];
|
|
int barrierCount = 0;
|
|
VERUS_FOR(i, dispatchMipCount)
|
|
{
|
|
const int subUAV = srcMip + i;
|
|
barriers[barrierCount++] = CD3DX12_RESOURCE_BARRIER::Transition(_uaResource._pResource.Get(),
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, subUAV);
|
|
}
|
|
pCmdList->ResourceBarrier(barrierCount, barriers);
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::transferDst, ImageLayout::xsReadOnly, Range(0, dispatchMipCount).OffsetBy(srcMip + 1));
|
|
}
|
|
|
|
srcMip += dispatchMipCount;
|
|
dispatchIndex++;
|
|
}
|
|
shader->EndBindDescriptors();
|
|
|
|
// Revert to main state:
|
|
if (_mainLayout != ImageLayout::xsReadOnly)
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::xsReadOnly, _mainLayout, Range(0, _desc._mipLevels));
|
|
|
|
ClearCshGenerateMips();
|
|
Schedule(0);
|
|
}
|
|
|
|
void TextureD3D12::GenerateCubeMapMips(PBaseCommandBuffer pCB)
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
|
|
if (!pCB)
|
|
pCB = renderer.GetCommandBuffer().Get();
|
|
auto pCmdList = static_cast<PCommandBufferD3D12>(pCB)->GetD3DGraphicsCommandList();
|
|
auto shader = renderer.GetShaderGenerateCubeMapMips();
|
|
auto& ub = renderer.GetUbGenerateCubeMapMips();
|
|
auto tex = TexturePtr::From(this);
|
|
|
|
const int maxMipLevel = _desc._mipLevels - 1;
|
|
|
|
// Transition state for sampling in compute shader:
|
|
if (_mainLayout != ImageLayout::xsReadOnly)
|
|
pCB->PipelineImageMemoryBarrier(tex, _mainLayout, ImageLayout::xsReadOnly, Range(0, _desc._mipLevels), Range(0, +CubeMapFace::count));
|
|
|
|
pCB->BindPipeline(renderer.GetPipelineGenerateCubeMapMips());
|
|
|
|
shader->BeginBindDescriptors();
|
|
int dispatchIndex = 0;
|
|
for (int srcMip = 0; srcMip < maxMipLevel;)
|
|
{
|
|
const int srcWidth = Math::Max(1, _desc._width >> srcMip);
|
|
const int srcHeight = Math::Max(1, _desc._height >> srcMip);
|
|
const int dstWidth = Math::Max(1, srcWidth >> 1);
|
|
const int dstHeight = Math::Max(1, srcHeight >> 1);
|
|
|
|
ub._srcMipLevel = srcMip;
|
|
ub._srgb = IsSRGBFormat(_desc._format);
|
|
ub._dstTexelSize.x = 1.f / dstWidth;
|
|
ub._dstTexelSize.y = 1.f / dstHeight;
|
|
|
|
int mipLevels[7] = {};
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
mipLevels[i + 1] = srcMip;
|
|
int arrayLayers[7] = {};
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
arrayLayers[i + 1] = ToNativeCubeMapFace(static_cast<CubeMapFace>(i));
|
|
const CSHandle complexSetHandle = shader->BindDescriptorSetTextures(0 | ShaderD3D12::SET_MOD_TO_VIEW_HEAP, { tex, tex, tex, tex, tex, tex, tex }, mipLevels, arrayLayers);
|
|
_vCshGenerateMips.push_back(complexSetHandle);
|
|
pCB->BindDescriptors(shader, 0, complexSetHandle);
|
|
|
|
pCB->Dispatch(Math::DivideByMultiple(dstWidth, 8), Math::DivideByMultiple(dstHeight, 8));
|
|
|
|
auto rb = CD3DX12_RESOURCE_BARRIER::UAV(_uaResource._pResource.Get());
|
|
pCmdList->ResourceBarrier(1, &rb);
|
|
|
|
// Transition state for upcoming CopyTextureRegion():
|
|
{
|
|
CD3DX12_RESOURCE_BARRIER barriers[+CubeMapFace::count];
|
|
int barrierCount = 0;
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
{
|
|
const int subUAV = D3D12CalcSubresource(srcMip, i, 0, _desc._mipLevels - 1, _desc._arrayLayers);
|
|
barriers[barrierCount++] = CD3DX12_RESOURCE_BARRIER::Transition(_uaResource._pResource.Get(),
|
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE, subUAV);
|
|
}
|
|
pCmdList->ResourceBarrier(barrierCount, barriers);
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::xsReadOnly, ImageLayout::transferDst, srcMip + 1, Range(0, +CubeMapFace::count));
|
|
}
|
|
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
{
|
|
const int subUAV = D3D12CalcSubresource(srcMip, i, 0, _desc._mipLevels - 1, _desc._arrayLayers);
|
|
const int subSRV = D3D12CalcSubresource(srcMip + 1, i, 0, _desc._mipLevels, _desc._arrayLayers);
|
|
const auto dstCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(_resource._pResource.Get(), subSRV);
|
|
const auto srcCopyLoc = CD3DX12_TEXTURE_COPY_LOCATION(_uaResource._pResource.Get(), subUAV);
|
|
pCmdList->CopyTextureRegion(
|
|
&dstCopyLoc,
|
|
0, 0, 0,
|
|
&srcCopyLoc,
|
|
nullptr);
|
|
}
|
|
|
|
// Transition state for next Dispatch():
|
|
{
|
|
CD3DX12_RESOURCE_BARRIER barriers[+CubeMapFace::count];
|
|
int barrierCount = 0;
|
|
VERUS_FOR(i, +CubeMapFace::count)
|
|
{
|
|
const int subUAV = D3D12CalcSubresource(srcMip, i, 0, _desc._mipLevels - 1, _desc._arrayLayers);
|
|
barriers[barrierCount++] = CD3DX12_RESOURCE_BARRIER::Transition(_uaResource._pResource.Get(),
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, subUAV);
|
|
}
|
|
pCmdList->ResourceBarrier(barrierCount, barriers);
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::transferDst, ImageLayout::xsReadOnly, srcMip + 1, Range(0, +CubeMapFace::count));
|
|
}
|
|
|
|
srcMip++;
|
|
dispatchIndex++;
|
|
}
|
|
shader->EndBindDescriptors();
|
|
|
|
// Revert to main state:
|
|
if (_mainLayout != ImageLayout::xsReadOnly)
|
|
pCB->PipelineImageMemoryBarrier(tex, ImageLayout::xsReadOnly, _mainLayout, Range(0, _desc._mipLevels), Range(0, +CubeMapFace::count));
|
|
|
|
ClearCshGenerateMips();
|
|
Schedule(0);
|
|
}
|
|
|
|
Continue TextureD3D12::Scheduled_Update()
|
|
{
|
|
if (!IsScheduledAllowed())
|
|
return Continue::yes;
|
|
|
|
for (auto& x : _vStagingBuffers)
|
|
{
|
|
VERUS_SMART_RELEASE(x._pMaAllocation);
|
|
VERUS_COM_RELEASE_CHECK(x._pResource.Get());
|
|
x._pResource.Reset();
|
|
}
|
|
_vStagingBuffers.clear();
|
|
|
|
ClearCshGenerateMips();
|
|
|
|
return Continue::no;
|
|
}
|
|
|
|
void TextureD3D12::ClearCshGenerateMips()
|
|
{
|
|
if (!_vCshGenerateMips.empty())
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
auto shader = renderer.GetShaderGenerateMips();
|
|
for (auto& csh : _vCshGenerateMips)
|
|
shader->FreeDescriptorSet(csh);
|
|
_vCshGenerateMips.clear();
|
|
}
|
|
}
|
|
|
|
void TextureD3D12::CreateSampler()
|
|
{
|
|
VERUS_QREF_RENDERER_D3D12;
|
|
VERUS_QREF_CONST_SETTINGS;
|
|
|
|
const bool tf = settings._gpuTrilinearFilter;
|
|
|
|
D3D12_SAMPLER_DESC samplerDesc = {};
|
|
|
|
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
|
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
|
|
if ('a' == _desc._pSamplerDesc->_filterMagMinMip[0])
|
|
{
|
|
if (settings._gpuAnisotropyLevel > 0)
|
|
samplerDesc.Filter = D3D12_FILTER_ANISOTROPIC;
|
|
else
|
|
samplerDesc.Filter = tf ? D3D12_FILTER_MIN_MAG_MIP_LINEAR : D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
}
|
|
else if ('s' == _desc._pSamplerDesc->_filterMagMinMip[0]) // Shadow map:
|
|
{
|
|
if (settings._sceneShadowQuality <= App::Settings::Quality::low)
|
|
samplerDesc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
|
|
else
|
|
samplerDesc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
|
|
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS;
|
|
}
|
|
else
|
|
{
|
|
if (!strcmp(_desc._pSamplerDesc->_filterMagMinMip, "nn"))
|
|
samplerDesc.Filter = tf ? D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR : D3D12_FILTER_MIN_MAG_MIP_POINT;
|
|
if (!strcmp(_desc._pSamplerDesc->_filterMagMinMip, "nl"))
|
|
samplerDesc.Filter = tf ? D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR : D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
|
|
if (!strcmp(_desc._pSamplerDesc->_filterMagMinMip, "ln"))
|
|
samplerDesc.Filter = tf ? D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR : D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
|
|
if (!strcmp(_desc._pSamplerDesc->_filterMagMinMip, "ll"))
|
|
samplerDesc.Filter = tf ? D3D12_FILTER_MIN_MAG_MIP_LINEAR : D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
}
|
|
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
VERUS_FOR(i, 3)
|
|
{
|
|
if (!_desc._pSamplerDesc->_addressModeUVW[i])
|
|
break;
|
|
D3D12_TEXTURE_ADDRESS_MODE tam = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
switch (_desc._pSamplerDesc->_addressModeUVW[i])
|
|
{
|
|
case 'r': tam = D3D12_TEXTURE_ADDRESS_MODE_WRAP; break;
|
|
case 'm': tam = D3D12_TEXTURE_ADDRESS_MODE_MIRROR; break;
|
|
case 'c': tam = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; break;
|
|
case 'b': tam = D3D12_TEXTURE_ADDRESS_MODE_BORDER; break;
|
|
}
|
|
switch (i)
|
|
{
|
|
case 0: samplerDesc.AddressU = tam; break;
|
|
case 1: samplerDesc.AddressV = tam; break;
|
|
case 2: samplerDesc.AddressW = tam; break;
|
|
}
|
|
}
|
|
samplerDesc.MipLODBias = _desc._pSamplerDesc->_mipLodBias;
|
|
samplerDesc.MaxAnisotropy = settings._gpuAnisotropyLevel;
|
|
samplerDesc.MinLOD = _desc._pSamplerDesc->_minLod;
|
|
samplerDesc.MaxLOD = _desc._pSamplerDesc->_maxLod;
|
|
memcpy(samplerDesc.BorderColor, _desc._pSamplerDesc->_borderColor.ToPointer(), sizeof(samplerDesc.BorderColor));
|
|
|
|
_dhSampler.Create(pRendererD3D12->GetD3DDevice(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1);
|
|
pRendererD3D12->GetD3DDevice()->CreateSampler(&samplerDesc, _dhSampler.AtCPU(0));
|
|
|
|
_desc._pSamplerDesc = nullptr;
|
|
}
|