Files
XCEngine/editor/app/Rendering/D3D12/D3D12UiTextureHost.cpp

745 lines
25 KiB
C++
Raw Normal View History

#include "D3D12UiTextureHost.h"
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
#include <XCEngine/RHI/D3D12/D3D12Enums.h>
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/RHITypes.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <cstdio>
#include <limits>
#include <sstream>
#include <utility>
namespace XCEngine::UI::Editor::Host {
namespace {
std::string HrToString(const char* operation, HRESULT hr) {
char buffer[128] = {};
sprintf_s(buffer, "%s failed with hr=0x%08X.", operation, static_cast<unsigned int>(hr));
return buffer;
}
constexpr std::uint32_t kTextureBytesPerPixel = 4u;
std::uint32_t AlignTextureRowPitch(std::uint32_t rowPitch) {
constexpr std::uint32_t alignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
return (rowPitch + alignment - 1u) & ~(alignment - 1u);
}
} // namespace
bool D3D12UiTextureHost::Initialize(D3D12WindowRenderer& windowRenderer) {
Shutdown();
if (windowRenderer.GetRHIDevice() == nullptr) {
m_lastError = "Initialize requires an initialized D3D12 window renderer.";
return false;
}
m_windowRenderer = &windowRenderer;
m_lastError.clear();
return true;
}
void D3D12UiTextureHost::BeginFrame(std::uint32_t frameSlot) {
if (frameSlot >= m_frameUploadBuffers.size()) {
m_hasActiveFrameSlot = false;
return;
}
if (!m_retiredTextures[frameSlot].empty()) {
std::ostringstream stream = {};
stream << "D3D12UiTextureHost::BeginFrame reclaim frameSlot=" << frameSlot
<< " retiredCount=" << m_retiredTextures[frameSlot].size();
AppendUIEditorRuntimeTrace("window-close", stream.str());
}
DestroyQueuedRetiredTextures(frameSlot);
m_frameUploadBuffers[frameSlot].clear();
m_activeFrameSlot = frameSlot;
m_hasActiveFrameSlot = true;
}
void D3D12UiTextureHost::Shutdown() {
std::size_t retiredTextureCount = 0u;
for (const auto& retiredTextures : m_retiredTextures) {
retiredTextureCount += retiredTextures.size();
}
{
std::ostringstream stream = {};
stream << "D3D12UiTextureHost::Shutdown begin liveTextures=" << m_liveTextures.size()
<< " retiredTextures=" << retiredTextureCount
<< " activeFrameSlot=" << m_activeFrameSlot
<< " hasActiveFrameSlot=" << (m_hasActiveFrameSlot ? 1 : 0);
AppendUIEditorRuntimeTrace("window-close", stream.str());
}
if (m_windowRenderer != nullptr) {
m_windowRenderer->WaitForGpuIdle();
}
for (auto& entry : m_liveTextures) {
if (entry.second != nullptr) {
DestroyTextureResource(*entry.second);
}
}
m_liveTextures.clear();
for (auto& retiredTextures : m_retiredTextures) {
for (auto& textureResource : retiredTextures) {
if (textureResource != nullptr) {
DestroyTextureResource(*textureResource);
}
}
retiredTextures.clear();
}
for (auto& uploadBuffers : m_frameUploadBuffers) {
uploadBuffers.clear();
}
m_wicFactory.Reset();
if (m_wicComInitialized) {
CoUninitialize();
m_wicComInitialized = false;
}
m_windowRenderer = nullptr;
m_activeFrameSlot = 0u;
m_hasActiveFrameSlot = false;
m_lastError.clear();
AppendUIEditorRuntimeTrace("window-close", "D3D12UiTextureHost::Shutdown end");
}
bool D3D12UiTextureHost::IsInitialized() const {
return m_windowRenderer != nullptr &&
m_windowRenderer->GetRHIDevice() != nullptr;
}
const std::string& D3D12UiTextureHost::GetLastError() const {
return m_lastError;
}
bool D3D12UiTextureHost::EnsureWicFactory(std::string& outError) {
outError.clear();
if (m_wicFactory != nullptr) {
return true;
}
const HRESULT initHr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(initHr) && initHr != RPC_E_CHANGED_MODE) {
outError = HrToString("CoInitializeEx", initHr);
return false;
}
if (SUCCEEDED(initHr)) {
m_wicComInitialized = true;
}
const HRESULT factoryHr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(m_wicFactory.ReleaseAndGetAddressOf()));
if (FAILED(factoryHr)) {
outError = HrToString("CoCreateInstance(CLSID_WICImagingFactory)", factoryHr);
return false;
}
return true;
}
bool D3D12UiTextureHost::DecodeTextureFile(
const std::filesystem::path& path,
std::vector<std::uint8_t>& outPixels,
std::uint32_t& outWidth,
std::uint32_t& outHeight,
std::string& outError) {
outPixels.clear();
outWidth = 0u;
outHeight = 0u;
outError.clear();
if (!EnsureWicFactory(outError)) {
return false;
}
Microsoft::WRL::ComPtr<IWICBitmapDecoder> decoder = {};
const HRESULT hr = m_wicFactory->CreateDecoderFromFilename(
path.wstring().c_str(),
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
decoder.ReleaseAndGetAddressOf());
if (FAILED(hr) || decoder == nullptr) {
outError = HrToString("IWICImagingFactory::CreateDecoderFromFilename", hr);
return false;
}
Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> frame = {};
const HRESULT frameHr = decoder->GetFrame(0u, frame.ReleaseAndGetAddressOf());
if (FAILED(frameHr) || frame == nullptr) {
outError = HrToString("IWICBitmapDecoder::GetFrame", frameHr);
return false;
}
return DecodeTextureFrame(*frame.Get(), outPixels, outWidth, outHeight, outError);
}
bool D3D12UiTextureHost::DecodeTextureMemory(
const std::uint8_t* data,
std::size_t size,
std::vector<std::uint8_t>& outPixels,
std::uint32_t& outWidth,
std::uint32_t& outHeight,
std::string& outError) {
outPixels.clear();
outWidth = 0u;
outHeight = 0u;
outError.clear();
if (data == nullptr || size == 0u) {
outError = "DecodeTextureMemory rejected an empty image payload.";
return false;
}
if (size > static_cast<std::size_t>((std::numeric_limits<DWORD>::max)())) {
outError = "DecodeTextureMemory payload exceeds WIC stream limits.";
return false;
}
if (!EnsureWicFactory(outError)) {
return false;
}
Microsoft::WRL::ComPtr<IWICStream> stream = {};
HRESULT hr = m_wicFactory->CreateStream(stream.ReleaseAndGetAddressOf());
if (FAILED(hr) || stream == nullptr) {
outError = HrToString("IWICImagingFactory::CreateStream", hr);
return false;
}
hr = stream->InitializeFromMemory(
const_cast<BYTE*>(reinterpret_cast<const BYTE*>(data)),
static_cast<DWORD>(size));
if (FAILED(hr)) {
outError = HrToString("IWICStream::InitializeFromMemory", hr);
return false;
}
Microsoft::WRL::ComPtr<IWICBitmapDecoder> decoder = {};
hr = m_wicFactory->CreateDecoderFromStream(
stream.Get(),
nullptr,
WICDecodeMetadataCacheOnLoad,
decoder.ReleaseAndGetAddressOf());
if (FAILED(hr) || decoder == nullptr) {
outError = HrToString("IWICImagingFactory::CreateDecoderFromStream", hr);
return false;
}
Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> frame = {};
hr = decoder->GetFrame(0u, frame.ReleaseAndGetAddressOf());
if (FAILED(hr) || frame == nullptr) {
outError = HrToString("IWICBitmapDecoder::GetFrame", hr);
return false;
}
return DecodeTextureFrame(*frame.Get(), outPixels, outWidth, outHeight, outError);
}
bool D3D12UiTextureHost::DecodeTextureFrame(
IWICBitmapSource& source,
std::vector<std::uint8_t>& outPixels,
std::uint32_t& outWidth,
std::uint32_t& outHeight,
std::string& outError) {
outPixels.clear();
outWidth = 0u;
outHeight = 0u;
outError.clear();
Microsoft::WRL::ComPtr<IWICFormatConverter> converter = {};
HRESULT hr = m_wicFactory->CreateFormatConverter(converter.ReleaseAndGetAddressOf());
if (FAILED(hr) || converter == nullptr) {
outError = HrToString("IWICImagingFactory::CreateFormatConverter", hr);
return false;
}
hr = converter->Initialize(
&source,
GUID_WICPixelFormat32bppRGBA,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom);
if (FAILED(hr)) {
outError = HrToString("IWICFormatConverter::Initialize", hr);
return false;
}
UINT width = 0u;
UINT height = 0u;
hr = converter->GetSize(&width, &height);
if (FAILED(hr) || width == 0u || height == 0u) {
outError = HrToString("IWICBitmapSource::GetSize", hr);
return false;
}
std::vector<std::uint8_t> pixels(
static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4u);
hr = converter->CopyPixels(
nullptr,
width * 4u,
static_cast<UINT>(pixels.size()),
pixels.data());
if (FAILED(hr)) {
outError = HrToString("IWICBitmapSource::CopyPixels", hr);
return false;
}
outPixels = std::move(pixels);
outWidth = static_cast<std::uint32_t>(width);
outHeight = static_cast<std::uint32_t>(height);
return true;
}
bool D3D12UiTextureHost::CreateTextureResource(
const std::uint8_t* rgbaPixels,
std::uint32_t width,
std::uint32_t height,
::XCEngine::UI::UITextureHandle& outTexture,
std::string& outError) {
ReleaseTexture(outTexture);
outTexture = {};
outError.clear();
if (!IsInitialized()) {
outError = "CreateTextureResource requires an initialized D3D12 UI texture host.";
return false;
}
if (rgbaPixels == nullptr || width == 0u || height == 0u) {
outError = "CreateTextureResource rejected an empty RGBA payload.";
return false;
}
::XCEngine::RHI::TextureDesc textureDesc = {};
textureDesc.width = width;
textureDesc.height = height;
textureDesc.depth = 1u;
textureDesc.mipLevels = 1u;
textureDesc.arraySize = 1u;
textureDesc.format = static_cast<std::uint32_t>(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
textureDesc.textureType =
static_cast<std::uint32_t>(::XCEngine::RHI::TextureType::Texture2D);
textureDesc.sampleCount = 1u;
textureDesc.sampleQuality = 0u;
textureDesc.flags = 0u;
const std::size_t pixelBytes =
static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4u;
::XCEngine::RHI::RHITexture* texture = nullptr;
Microsoft::WRL::ComPtr<ID3D12Resource> uploadBuffer = {};
if (m_hasActiveFrameSlot && m_windowRenderer->GetDevice() != nullptr) {
const ::XCEngine::Rendering::RenderContext renderContext =
m_windowRenderer->GetRenderContext();
auto* d3d12CommandList =
renderContext.commandList != nullptr
? static_cast<::XCEngine::RHI::D3D12CommandList*>(renderContext.commandList)
: nullptr;
if (d3d12CommandList != nullptr && d3d12CommandList->GetCommandList() != nullptr) {
auto* d3d12Texture = new ::XCEngine::RHI::D3D12Texture();
D3D12_RESOURCE_DESC nativeTextureDesc = {};
nativeTextureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
nativeTextureDesc.Alignment = 0;
nativeTextureDesc.Width = width;
nativeTextureDesc.Height = height;
nativeTextureDesc.DepthOrArraySize = 1u;
nativeTextureDesc.MipLevels = 1u;
nativeTextureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
nativeTextureDesc.SampleDesc.Count = 1u;
nativeTextureDesc.SampleDesc.Quality = 0u;
nativeTextureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
nativeTextureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (d3d12Texture->InitializeFromData(
m_windowRenderer->GetDevice(),
d3d12CommandList->GetCommandList(),
nativeTextureDesc,
::XCEngine::RHI::TextureType::Texture2D,
rgbaPixels,
pixelBytes,
width * 4u,
&uploadBuffer)) {
texture = d3d12Texture;
} else {
delete d3d12Texture;
}
}
}
if (texture == nullptr) {
texture = m_windowRenderer->GetRHIDevice()->CreateTexture(
textureDesc,
rgbaPixels,
pixelBytes,
width * 4u);
}
if (texture == nullptr) {
outError = "Failed to create the GPU texture.";
return false;
}
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {};
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {};
if (!m_windowRenderer->GetTextureDescriptorAllocator().CreateTextureDescriptor(
texture,
&cpuHandle,
&gpuHandle) ||
cpuHandle.ptr == 0u ||
gpuHandle.ptr == 0u) {
texture->Shutdown();
delete texture;
outError = "Failed to allocate the shader resource descriptor for the GPU texture.";
return false;
}
auto textureResource = std::make_unique<TextureResource>();
textureResource->texture = texture;
textureResource->cpuHandle = cpuHandle;
textureResource->gpuHandle = gpuHandle;
textureResource->width = width;
textureResource->height = height;
if (uploadBuffer != nullptr &&
m_hasActiveFrameSlot &&
m_activeFrameSlot < m_frameUploadBuffers.size()) {
m_frameUploadBuffers[m_activeFrameSlot].push_back(std::move(uploadBuffer));
}
const std::uintptr_t resourceKey =
reinterpret_cast<std::uintptr_t>(textureResource.get());
outTexture.nativeHandle = static_cast<std::uintptr_t>(gpuHandle.ptr);
outTexture.width = width;
outTexture.height = height;
outTexture.kind = ::XCEngine::UI::UITextureHandleKind::ShaderResourceView;
outTexture.resourceHandle = resourceKey;
m_liveTextures.emplace(resourceKey, std::move(textureResource));
return true;
}
void D3D12UiTextureHost::DestroyQueuedRetiredTextures(std::uint32_t frameSlot) {
if (frameSlot >= m_retiredTextures.size()) {
return;
}
for (auto& textureResource : m_retiredTextures[frameSlot]) {
if (textureResource != nullptr) {
DestroyTextureResource(*textureResource);
}
}
m_retiredTextures[frameSlot].clear();
}
D3D12UiTextureHost::TextureResource* D3D12UiTextureHost::ResolveTextureResource(
const ::XCEngine::UI::UITextureHandle& texture) const {
if (!texture.IsValid() || texture.resourceHandle == 0u) {
return nullptr;
}
const auto found = m_liveTextures.find(texture.resourceHandle);
if (found == m_liveTextures.end() || found->second == nullptr) {
return nullptr;
}
return found->second.get();
}
void D3D12UiTextureHost::DestroyTextureResource(TextureResource& textureResource) {
if (m_windowRenderer != nullptr &&
textureResource.cpuHandle.ptr != 0u &&
textureResource.gpuHandle.ptr != 0u) {
m_windowRenderer->GetTextureDescriptorAllocator().Free(
textureResource.cpuHandle,
textureResource.gpuHandle);
}
if (textureResource.texture != nullptr) {
textureResource.texture->Shutdown();
delete textureResource.texture;
textureResource.texture = nullptr;
}
textureResource.cpuHandle = {};
textureResource.gpuHandle = {};
textureResource.width = 0u;
textureResource.height = 0u;
}
void D3D12UiTextureHost::RetireTextureResource(
std::unique_ptr<TextureResource> textureResource) {
if (textureResource == nullptr) {
return;
}
if (m_hasActiveFrameSlot && m_activeFrameSlot < m_retiredTextures.size()) {
m_retiredTextures[m_activeFrameSlot].push_back(std::move(textureResource));
return;
}
AppendUIEditorRuntimeTrace(
"window-close",
"D3D12UiTextureHost::RetireTextureResource forcing WaitForGpuIdle for immediate destruction");
if (m_windowRenderer != nullptr) {
m_windowRenderer->WaitForGpuIdle();
}
DestroyTextureResource(*textureResource);
}
bool D3D12UiTextureHost::UpdateTextureRegionRgba(
const ::XCEngine::UI::UITextureHandle& texture,
std::uint32_t dstX,
std::uint32_t dstY,
const std::uint8_t* rgbaPixels,
std::uint32_t width,
std::uint32_t height,
std::uint32_t rowPitch,
std::string& outError) {
outError.clear();
if (!IsInitialized()) {
outError = "UpdateTextureRegionRgba requires an initialized D3D12 UI texture host.";
return false;
}
if (!m_hasActiveFrameSlot || m_activeFrameSlot >= m_frameUploadBuffers.size()) {
outError = "UpdateTextureRegionRgba requires an active frame slot.";
return false;
}
if (rgbaPixels == nullptr || width == 0u || height == 0u) {
outError = "UpdateTextureRegionRgba rejected an empty RGBA payload.";
return false;
}
TextureResource* textureResource = ResolveTextureResource(texture);
if (textureResource == nullptr || textureResource->texture == nullptr) {
outError = "UpdateTextureRegionRgba could not resolve the destination texture.";
return false;
}
if (dstX + width > textureResource->width || dstY + height > textureResource->height) {
outError = "UpdateTextureRegionRgba target rectangle exceeds texture bounds.";
return false;
}
const ::XCEngine::Rendering::RenderContext renderContext = m_windowRenderer->GetRenderContext();
auto* d3d12CommandList =
renderContext.commandList != nullptr
? static_cast<::XCEngine::RHI::D3D12CommandList*>(renderContext.commandList)
: nullptr;
if (d3d12CommandList == nullptr || d3d12CommandList->GetCommandList() == nullptr) {
outError = "UpdateTextureRegionRgba requires an active D3D12 command list.";
return false;
}
auto* d3d12Texture =
dynamic_cast<::XCEngine::RHI::D3D12Texture*>(textureResource->texture);
if (d3d12Texture == nullptr || d3d12Texture->GetResource() == nullptr) {
outError = "UpdateTextureRegionRgba requires a native D3D12 destination texture.";
return false;
}
const std::uint32_t resolvedRowPitch =
rowPitch > 0u ? rowPitch : width * kTextureBytesPerPixel;
if (resolvedRowPitch < width * kTextureBytesPerPixel) {
outError = "UpdateTextureRegionRgba row pitch is smaller than the texture row width.";
return false;
}
const std::uint32_t alignedRowPitch = AlignTextureRowPitch(width * kTextureBytesPerPixel);
const std::uint64_t uploadBytes =
static_cast<std::uint64_t>(alignedRowPitch) * static_cast<std::uint64_t>(height);
D3D12_HEAP_PROPERTIES uploadHeapProperties = {};
uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC uploadDesc = {};
uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
uploadDesc.Alignment = 0;
uploadDesc.Width = uploadBytes;
uploadDesc.Height = 1;
uploadDesc.DepthOrArraySize = 1;
uploadDesc.MipLevels = 1;
uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
uploadDesc.SampleDesc.Count = 1;
uploadDesc.SampleDesc.Quality = 0;
uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
Microsoft::WRL::ComPtr<ID3D12Resource> uploadBuffer = {};
HRESULT hr = m_windowRenderer->GetDevice()->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&uploadDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(uploadBuffer.ReleaseAndGetAddressOf()));
if (FAILED(hr) || uploadBuffer == nullptr) {
outError = HrToString("ID3D12Device::CreateCommittedResource(upload)", hr);
return false;
}
std::uint8_t* mappedData = nullptr;
hr = uploadBuffer->Map(0u, nullptr, reinterpret_cast<void**>(&mappedData));
if (FAILED(hr) || mappedData == nullptr) {
outError = HrToString("ID3D12Resource::Map(upload)", hr);
return false;
}
for (std::uint32_t row = 0u; row < height; ++row) {
std::memcpy(
mappedData + static_cast<std::size_t>(row) * alignedRowPitch,
rgbaPixels + static_cast<std::size_t>(row) * resolvedRowPitch,
static_cast<std::size_t>(width) * kTextureBytesPerPixel);
}
uploadBuffer->Unmap(0u, nullptr);
ID3D12GraphicsCommandList* nativeCommandList = d3d12CommandList->GetCommandList();
const ::XCEngine::RHI::ResourceStates originalState = d3d12Texture->GetState();
if (originalState != ::XCEngine::RHI::ResourceStates::CopyDst) {
D3D12_RESOURCE_BARRIER toCopyBarrier = {};
toCopyBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
toCopyBarrier.Transition.pResource = d3d12Texture->GetResource();
toCopyBarrier.Transition.StateBefore = ::XCEngine::RHI::ToD3D12(originalState);
toCopyBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
toCopyBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
nativeCommandList->ResourceBarrier(1u, &toCopyBarrier);
d3d12Texture->SetState(::XCEngine::RHI::ResourceStates::CopyDst);
}
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = d3d12Texture->GetResource();
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstLocation.SubresourceIndex = 0u;
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = uploadBuffer.Get();
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcLocation.PlacedFootprint.Offset = 0u;
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srcLocation.PlacedFootprint.Footprint.Width = width;
srcLocation.PlacedFootprint.Footprint.Height = height;
srcLocation.PlacedFootprint.Footprint.Depth = 1u;
srcLocation.PlacedFootprint.Footprint.RowPitch = alignedRowPitch;
D3D12_BOX srcBox = {};
srcBox.left = 0u;
srcBox.top = 0u;
srcBox.front = 0u;
srcBox.right = width;
srcBox.bottom = height;
srcBox.back = 1u;
nativeCommandList->CopyTextureRegion(
&dstLocation,
dstX,
dstY,
0u,
&srcLocation,
&srcBox);
if (originalState != ::XCEngine::RHI::ResourceStates::CopyDst) {
D3D12_RESOURCE_BARRIER restoreBarrier = {};
restoreBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
restoreBarrier.Transition.pResource = d3d12Texture->GetResource();
restoreBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
restoreBarrier.Transition.StateAfter = ::XCEngine::RHI::ToD3D12(originalState);
restoreBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
nativeCommandList->ResourceBarrier(1u, &restoreBarrier);
d3d12Texture->SetState(originalState);
}
m_frameUploadBuffers[m_activeFrameSlot].push_back(std::move(uploadBuffer));
return true;
}
bool D3D12UiTextureHost::LoadTextureFromFile(
const std::filesystem::path& path,
::XCEngine::UI::UITextureHandle& outTexture,
std::string& outError) {
std::vector<std::uint8_t> pixels = {};
std::uint32_t width = 0u;
std::uint32_t height = 0u;
if (!DecodeTextureFile(path, pixels, width, height, outError)) {
outTexture = {};
m_lastError = outError;
return false;
}
const bool created = CreateTextureResource(
pixels.data(),
width,
height,
outTexture,
outError);
m_lastError = created ? std::string() : outError;
return created;
}
bool D3D12UiTextureHost::LoadTextureFromMemory(
const std::uint8_t* data,
std::size_t size,
::XCEngine::UI::UITextureHandle& outTexture,
std::string& outError) {
std::vector<std::uint8_t> pixels = {};
std::uint32_t width = 0u;
std::uint32_t height = 0u;
if (!DecodeTextureMemory(data, size, pixels, width, height, outError)) {
outTexture = {};
m_lastError = outError;
return false;
}
const bool created = CreateTextureResource(
pixels.data(),
width,
height,
outTexture,
outError);
m_lastError = created ? std::string() : outError;
return created;
}
bool D3D12UiTextureHost::LoadTextureFromRgba(
const std::uint8_t* rgbaPixels,
std::uint32_t width,
std::uint32_t height,
::XCEngine::UI::UITextureHandle& outTexture,
std::string& outError) {
const bool created = CreateTextureResource(
rgbaPixels,
width,
height,
outTexture,
outError);
m_lastError = created ? std::string() : outError;
return created;
}
void D3D12UiTextureHost::ReleaseTexture(::XCEngine::UI::UITextureHandle& texture) {
if (!texture.IsValid()) {
texture = {};
return;
}
const auto found = m_liveTextures.find(texture.resourceHandle);
if (found != m_liveTextures.end() && found->second != nullptr) {
std::unique_ptr<TextureResource> retiredTexture = std::move(found->second);
m_liveTextures.erase(found);
RetireTextureResource(std::move(retiredTexture));
}
texture = {};
}
} // namespace XCEngine::UI::Editor::Host