refactor: move built-in icon upload into imgui backend
This commit is contained in:
32
editor/src/UI/BuiltInIconLayoutUtils.h
Normal file
32
editor/src/UI/BuiltInIconLayoutUtils.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Editor {
|
||||||
|
namespace UI {
|
||||||
|
|
||||||
|
inline ImVec2 ComputeFittedIconSize(
|
||||||
|
int textureWidth,
|
||||||
|
int textureHeight,
|
||||||
|
const ImVec2& min,
|
||||||
|
const ImVec2& max) {
|
||||||
|
const float availableWidth = max.x - min.x;
|
||||||
|
const float availableHeight = max.y - min.y;
|
||||||
|
if (availableWidth <= 0.0f || availableHeight <= 0.0f || textureWidth <= 0 || textureHeight <= 0) {
|
||||||
|
return ImVec2(0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float scale = (std::min)(
|
||||||
|
availableWidth / static_cast<float>(textureWidth),
|
||||||
|
availableHeight / static_cast<float>(textureHeight));
|
||||||
|
return ImVec2(
|
||||||
|
static_cast<float>(textureWidth) * scale,
|
||||||
|
static_cast<float>(textureHeight) * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "BuiltInIcons.h"
|
#include "BuiltInIcons.h"
|
||||||
|
|
||||||
#include "ImGuiBackendBridge.h"
|
#include "ImGuiBackendBridge.h"
|
||||||
|
#include "BuiltInIconLayoutUtils.h"
|
||||||
#include "Platform/Win32Utf8.h"
|
#include "Platform/Win32Utf8.h"
|
||||||
#include "StyleTokens.h"
|
#include "StyleTokens.h"
|
||||||
|
|
||||||
@@ -19,7 +20,6 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wrl/client.h>
|
|
||||||
|
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
|
|
||||||
@@ -29,20 +29,8 @@ namespace UI {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Microsoft::WRL::ComPtr;
|
using BuiltInTexture = ImGuiBackendBridge::UploadedTexture;
|
||||||
|
using PreviewGpuUpload = ImGuiBackendBridge::PendingTextureUpload;
|
||||||
struct BuiltInTexture {
|
|
||||||
ImTextureID textureId = {};
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {};
|
|
||||||
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {};
|
|
||||||
ComPtr<ID3D12Resource> texture;
|
|
||||||
int width = 0;
|
|
||||||
int height = 0;
|
|
||||||
|
|
||||||
bool IsValid() const {
|
|
||||||
return textureId != ImTextureID{} && texture != nullptr && width > 0 && height > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LoadedTexturePixels {
|
struct LoadedTexturePixels {
|
||||||
std::vector<stbi_uc> rgbaPixels;
|
std::vector<stbi_uc> rgbaPixels;
|
||||||
@@ -50,13 +38,6 @@ struct LoadedTexturePixels {
|
|||||||
int height = 0;
|
int height = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreviewGpuUpload {
|
|
||||||
ComPtr<ID3D12Resource> uploadResource;
|
|
||||||
ComPtr<ID3D12CommandAllocator> commandAllocator;
|
|
||||||
ComPtr<ID3D12GraphicsCommandList> commandList;
|
|
||||||
UINT64 fenceValue = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PreviewDecodeJob {
|
struct PreviewDecodeJob {
|
||||||
std::string key;
|
std::string key;
|
||||||
std::filesystem::path filePath;
|
std::filesystem::path filePath;
|
||||||
@@ -137,8 +118,6 @@ struct BuiltInIconState {
|
|||||||
std::condition_variable previewQueueEvent;
|
std::condition_variable previewQueueEvent;
|
||||||
bool previewWorkersRunning = false;
|
bool previewWorkersRunning = false;
|
||||||
size_t pendingPreviewDecodeJobs = 0;
|
size_t pendingPreviewDecodeJobs = 0;
|
||||||
ComPtr<ID3D12Fence> uploadFence;
|
|
||||||
UINT64 nextUploadFenceValue = 1;
|
|
||||||
int lastPreviewBudgetFrame = -1;
|
int lastPreviewBudgetFrame = -1;
|
||||||
int previewLoadsThisFrame = 0;
|
int previewLoadsThisFrame = 0;
|
||||||
int lastMaintenanceFrame = -1;
|
int lastMaintenanceFrame = -1;
|
||||||
@@ -560,16 +539,12 @@ bool RefreshAssetPreviewSourceFingerprint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ResetTexture(BuiltInTexture& texture) {
|
void ResetTexture(BuiltInTexture& texture) {
|
||||||
if (g_icons.backend && texture.cpuHandle.ptr != 0) {
|
if (g_icons.backend != nullptr) {
|
||||||
g_icons.backend->FreeTextureDescriptor(texture.cpuHandle, texture.gpuHandle);
|
g_icons.backend->ResetUploadedTexture(&texture);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture.texture.Reset();
|
texture = {};
|
||||||
texture.textureId = {};
|
|
||||||
texture.cpuHandle = {};
|
|
||||||
texture.gpuHandle = {};
|
|
||||||
texture.width = 0;
|
|
||||||
texture.height = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetAssetPreviewCache() {
|
void ResetAssetPreviewCache() {
|
||||||
@@ -579,48 +554,6 @@ void ResetAssetPreviewCache() {
|
|||||||
g_icons.assetPreviews.clear();
|
g_icons.assetPreviews.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnsureUploadFence(ID3D12Device* device) {
|
|
||||||
if (g_icons.uploadFence) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCEEDED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_icons.uploadFence)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WaitForQueueIdle(ID3D12Device* device, ID3D12CommandQueue* commandQueue) {
|
|
||||||
if (!device || !commandQueue) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<ID3D12Fence> fence;
|
|
||||||
if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE eventHandle = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
|
||||||
if (!eventHandle) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr UINT64 kFenceValue = 1;
|
|
||||||
const HRESULT signalHr = commandQueue->Signal(fence.Get(), kFenceValue);
|
|
||||||
if (FAILED(signalHr)) {
|
|
||||||
CloseHandle(eventHandle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fence->GetCompletedValue() < kFenceValue) {
|
|
||||||
if (FAILED(fence->SetEventOnCompletion(kFenceValue, eventHandle))) {
|
|
||||||
CloseHandle(eventHandle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
WaitForSingleObject(eventHandle, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(eventHandle);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DecodeTextureFromFile(
|
bool DecodeTextureFromFile(
|
||||||
const std::filesystem::path& filePath,
|
const std::filesystem::path& filePath,
|
||||||
LoadedTexturePixels& outTexturePixels) {
|
LoadedTexturePixels& outTexturePixels) {
|
||||||
@@ -660,163 +593,6 @@ bool DecodeTextureFromFile(
|
|||||||
return DownscalePreviewTextureIfNeeded(outTexturePixels);
|
return DownscalePreviewTextureIfNeeded(outTexturePixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UploadTexturePixels(
|
|
||||||
ImGuiBackendBridge& backend,
|
|
||||||
ID3D12Device* device,
|
|
||||||
ID3D12CommandQueue* commandQueue,
|
|
||||||
const LoadedTexturePixels& texturePixels,
|
|
||||||
BuiltInTexture& outTexture,
|
|
||||||
std::unique_ptr<PreviewGpuUpload>& outPendingUpload) {
|
|
||||||
if (!device || !commandQueue || texturePixels.width <= 0 || texturePixels.height <= 0 || texturePixels.rgbaPixels.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UINT srcRowPitch = static_cast<UINT>(texturePixels.width * 4);
|
|
||||||
|
|
||||||
D3D12_RESOURCE_DESC textureDesc = {};
|
|
||||||
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
||||||
textureDesc.Alignment = 0;
|
|
||||||
textureDesc.Width = static_cast<UINT64>(texturePixels.width);
|
|
||||||
textureDesc.Height = static_cast<UINT>(texturePixels.height);
|
|
||||||
textureDesc.DepthOrArraySize = 1;
|
|
||||||
textureDesc.MipLevels = 1;
|
|
||||||
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
textureDesc.SampleDesc.Count = 1;
|
|
||||||
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
||||||
|
|
||||||
D3D12_HEAP_PROPERTIES defaultHeap = {};
|
|
||||||
defaultHeap.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
||||||
|
|
||||||
ComPtr<ID3D12Resource> textureResource;
|
|
||||||
if (FAILED(device->CreateCommittedResource(
|
|
||||||
&defaultHeap,
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&textureDesc,
|
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
||||||
nullptr,
|
|
||||||
IID_PPV_ARGS(&textureResource)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
|
|
||||||
UINT numRows = 0;
|
|
||||||
UINT64 rowSizeInBytes = 0;
|
|
||||||
UINT64 uploadBufferSize = 0;
|
|
||||||
device->GetCopyableFootprints(&textureDesc, 0, 1, 0, &footprint, &numRows, &rowSizeInBytes, &uploadBufferSize);
|
|
||||||
|
|
||||||
D3D12_RESOURCE_DESC uploadDesc = {};
|
|
||||||
uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
||||||
uploadDesc.Width = uploadBufferSize;
|
|
||||||
uploadDesc.Height = 1;
|
|
||||||
uploadDesc.DepthOrArraySize = 1;
|
|
||||||
uploadDesc.MipLevels = 1;
|
|
||||||
uploadDesc.SampleDesc.Count = 1;
|
|
||||||
uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
||||||
|
|
||||||
D3D12_HEAP_PROPERTIES uploadHeap = {};
|
|
||||||
uploadHeap.Type = D3D12_HEAP_TYPE_UPLOAD;
|
|
||||||
|
|
||||||
ComPtr<ID3D12Resource> uploadResource;
|
|
||||||
if (FAILED(device->CreateCommittedResource(
|
|
||||||
&uploadHeap,
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&uploadDesc,
|
|
||||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
||||||
nullptr,
|
|
||||||
IID_PPV_ARGS(&uploadResource)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t* mappedData = nullptr;
|
|
||||||
if (FAILED(uploadResource->Map(0, nullptr, reinterpret_cast<void**>(&mappedData)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (UINT row = 0; row < numRows; ++row) {
|
|
||||||
std::memcpy(
|
|
||||||
mappedData + footprint.Offset + static_cast<SIZE_T>(row) * footprint.Footprint.RowPitch,
|
|
||||||
texturePixels.rgbaPixels.data() + static_cast<size_t>(row) * srcRowPitch,
|
|
||||||
srcRowPitch);
|
|
||||||
}
|
|
||||||
uploadResource->Unmap(0, nullptr);
|
|
||||||
|
|
||||||
ComPtr<ID3D12CommandAllocator> commandAllocator;
|
|
||||||
ComPtr<ID3D12GraphicsCommandList> commandList;
|
|
||||||
if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (FAILED(device->CreateCommandList(
|
|
||||||
0,
|
|
||||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
commandAllocator.Get(),
|
|
||||||
nullptr,
|
|
||||||
IID_PPV_ARGS(&commandList)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION dst = {};
|
|
||||||
dst.pResource = textureResource.Get();
|
|
||||||
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
||||||
dst.SubresourceIndex = 0;
|
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION src = {};
|
|
||||||
src.pResource = uploadResource.Get();
|
|
||||||
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
||||||
src.PlacedFootprint = footprint;
|
|
||||||
|
|
||||||
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
|
|
||||||
|
|
||||||
D3D12_RESOURCE_BARRIER barrier = {};
|
|
||||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
|
||||||
barrier.Transition.pResource = textureResource.Get();
|
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
|
||||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
|
||||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
||||||
commandList->ResourceBarrier(1, &barrier);
|
|
||||||
|
|
||||||
if (FAILED(commandList->Close())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EnsureUploadFence(device)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetTexture(outTexture);
|
|
||||||
backend.AllocateTextureDescriptor(&outTexture.cpuHandle, &outTexture.gpuHandle);
|
|
||||||
if (outTexture.cpuHandle.ptr == 0 || outTexture.gpuHandle.ptr == 0) {
|
|
||||||
ResetTexture(outTexture);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
|
||||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
|
||||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
|
||||||
srvDesc.Texture2D.MipLevels = 1;
|
|
||||||
device->CreateShaderResourceView(textureResource.Get(), &srvDesc, outTexture.cpuHandle);
|
|
||||||
|
|
||||||
outTexture.texture = textureResource;
|
|
||||||
outTexture.textureId = (ImTextureID)(static_cast<intptr_t>(outTexture.gpuHandle.ptr));
|
|
||||||
outTexture.width = texturePixels.width;
|
|
||||||
outTexture.height = texturePixels.height;
|
|
||||||
|
|
||||||
ID3D12CommandList* commandLists[] = { commandList.Get() };
|
|
||||||
commandQueue->ExecuteCommandLists(1, commandLists);
|
|
||||||
const UINT64 fenceValue = g_icons.nextUploadFenceValue++;
|
|
||||||
if (FAILED(commandQueue->Signal(g_icons.uploadFence.Get(), fenceValue))) {
|
|
||||||
ResetTexture(outTexture);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
outPendingUpload = std::make_unique<PreviewGpuUpload>();
|
|
||||||
outPendingUpload->uploadResource = std::move(uploadResource);
|
|
||||||
outPendingUpload->commandAllocator = std::move(commandAllocator);
|
|
||||||
outPendingUpload->commandList = std::move(commandList);
|
|
||||||
outPendingUpload->fenceValue = fenceValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LoadTextureFromFile(
|
bool LoadTextureFromFile(
|
||||||
ImGuiBackendBridge& backend,
|
ImGuiBackendBridge& backend,
|
||||||
ID3D12Device* device,
|
ID3D12Device* device,
|
||||||
@@ -829,13 +605,14 @@ bool LoadTextureFromFile(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return UploadTexturePixels(
|
return backend.UploadRgbaTexture(
|
||||||
backend,
|
|
||||||
device,
|
device,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
texturePixels,
|
texturePixels.rgbaPixels.data(),
|
||||||
outTexture,
|
texturePixels.width,
|
||||||
outPendingUpload);
|
texturePixels.height,
|
||||||
|
&outTexture,
|
||||||
|
&outPendingUpload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewWorkerMain() {
|
void PreviewWorkerMain() {
|
||||||
@@ -985,15 +762,13 @@ void DrainPreviewDecodeResults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PollCompletedUploads() {
|
void PollCompletedUploads() {
|
||||||
if (!g_icons.uploadFence) {
|
if (g_icons.backend == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UINT64 completedFenceValue = g_icons.uploadFence->GetCompletedValue();
|
|
||||||
|
|
||||||
for (auto& entry : g_icons.assetPreviews) {
|
for (auto& entry : g_icons.assetPreviews) {
|
||||||
auto& preview = entry.second;
|
auto& preview = entry.second;
|
||||||
if (preview.pendingUpload && completedFenceValue >= preview.pendingUpload->fenceValue) {
|
if (preview.pendingUpload && g_icons.backend->IsTextureUploadComplete(*preview.pendingUpload)) {
|
||||||
preview.pendingUpload.reset();
|
preview.pendingUpload.reset();
|
||||||
if (preview.hasStaleTexture) {
|
if (preview.hasStaleTexture) {
|
||||||
ResetTexture(preview.texture);
|
ResetTexture(preview.texture);
|
||||||
@@ -1002,13 +777,7 @@ void PollCompletedUploads() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto eraseIt = std::remove_if(
|
g_icons.backend->RemoveCompletedTextureUploads(&g_icons.pendingIconUploads);
|
||||||
g_icons.pendingIconUploads.begin(),
|
|
||||||
g_icons.pendingIconUploads.end(),
|
|
||||||
[completedFenceValue](const std::unique_ptr<PreviewGpuUpload>& upload) {
|
|
||||||
return upload && completedFenceValue >= upload->fenceValue;
|
|
||||||
});
|
|
||||||
g_icons.pendingIconUploads.erase(eraseIt, g_icons.pendingIconUploads.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaintainIconRuntimeState() {
|
void MaintainIconRuntimeState() {
|
||||||
@@ -1022,27 +791,12 @@ void MaintainIconRuntimeState() {
|
|||||||
PollCompletedUploads();
|
PollCompletedUploads();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 ComputeFittedIconSize(const BuiltInTexture& texture, const ImVec2& min, const ImVec2& max) {
|
|
||||||
const float availableWidth = max.x - min.x;
|
|
||||||
const float availableHeight = max.y - min.y;
|
|
||||||
if (availableWidth <= 0.0f || availableHeight <= 0.0f || texture.width <= 0 || texture.height <= 0) {
|
|
||||||
return ImVec2(0.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
const float scale = (std::min)(
|
|
||||||
availableWidth / static_cast<float>(texture.width),
|
|
||||||
availableHeight / static_cast<float>(texture.height));
|
|
||||||
return ImVec2(
|
|
||||||
static_cast<float>(texture.width) * scale,
|
|
||||||
static_cast<float>(texture.height) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawTextureIcon(ImDrawList* drawList, const BuiltInTexture& texture, const ImVec2& min, const ImVec2& max) {
|
void DrawTextureIcon(ImDrawList* drawList, const BuiltInTexture& texture, const ImVec2& min, const ImVec2& max) {
|
||||||
if (!drawList || !texture.IsValid()) {
|
if (!drawList || !texture.IsValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 size = ComputeFittedIconSize(texture, min, max);
|
const ImVec2 size = ComputeFittedIconSize(texture.width, texture.height, min, max);
|
||||||
const float x = min.x + ((max.x - min.x) - size.x) * 0.5f;
|
const float x = min.x + ((max.x - min.x) - size.x) * 0.5f;
|
||||||
const float y = min.y + ((max.y - min.y) - size.y) * 0.5f;
|
const float y = min.y + ((max.y - min.y) - size.y) * 0.5f;
|
||||||
drawList->AddImage(texture.textureId, ImVec2(x, y), ImVec2(x + size.x, y + size.y));
|
drawList->AddImage(texture.textureId, ImVec2(x, y), ImVec2(x + size.x, y + size.y));
|
||||||
@@ -1126,7 +880,7 @@ BuiltInIconState::CachedAssetPreview* GetOrCreateAssetPreview(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (preview.texture.IsValid() && !preview.hasStaleTexture) {
|
if (preview.texture.IsValid() && !preview.hasStaleTexture) {
|
||||||
if (preview.pendingUpload && preview.pendingUpload->fenceValue <= g_icons.uploadFence->GetCompletedValue()) {
|
if (preview.pendingUpload && g_icons.backend->IsTextureUploadComplete(*preview.pendingUpload)) {
|
||||||
preview.pendingUpload.reset();
|
preview.pendingUpload.reset();
|
||||||
}
|
}
|
||||||
return &preview;
|
return &preview;
|
||||||
@@ -1134,13 +888,14 @@ BuiltInIconState::CachedAssetPreview* GetOrCreateAssetPreview(
|
|||||||
|
|
||||||
if (preview.decodedPixels && !preview.pendingUpload) {
|
if (preview.decodedPixels && !preview.pendingUpload) {
|
||||||
std::unique_ptr<PreviewGpuUpload> pendingUpload;
|
std::unique_ptr<PreviewGpuUpload> pendingUpload;
|
||||||
if (!UploadTexturePixels(
|
if (!g_icons.backend->UploadRgbaTexture(
|
||||||
*g_icons.backend,
|
|
||||||
g_icons.device,
|
g_icons.device,
|
||||||
g_icons.commandQueue,
|
g_icons.commandQueue,
|
||||||
*preview.decodedPixels,
|
preview.decodedPixels->rgbaPixels.data(),
|
||||||
preview.texture,
|
preview.decodedPixels->width,
|
||||||
pendingUpload)) {
|
preview.decodedPixels->height,
|
||||||
|
&preview.texture,
|
||||||
|
&pendingUpload)) {
|
||||||
preview.decodedPixels.reset();
|
preview.decodedPixels.reset();
|
||||||
preview.loadFailed = true;
|
preview.loadFailed = true;
|
||||||
return &preview;
|
return &preview;
|
||||||
@@ -1233,8 +988,8 @@ void InitializeBuiltInIcons(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownBuiltInIcons() {
|
void ShutdownBuiltInIcons() {
|
||||||
if (g_icons.device && g_icons.commandQueue) {
|
if (g_icons.backend && g_icons.device && g_icons.commandQueue) {
|
||||||
WaitForQueueIdle(g_icons.device, g_icons.commandQueue);
|
g_icons.backend->WaitForQueueIdle(g_icons.device, g_icons.commandQueue);
|
||||||
}
|
}
|
||||||
StopPreviewWorkers();
|
StopPreviewWorkers();
|
||||||
ResetAssetPreviewCache();
|
ResetAssetPreviewCache();
|
||||||
@@ -1245,8 +1000,6 @@ void ShutdownBuiltInIcons() {
|
|||||||
g_icons.backend = nullptr;
|
g_icons.backend = nullptr;
|
||||||
g_icons.device = nullptr;
|
g_icons.device = nullptr;
|
||||||
g_icons.commandQueue = nullptr;
|
g_icons.commandQueue = nullptr;
|
||||||
g_icons.uploadFence.Reset();
|
|
||||||
g_icons.nextUploadFenceValue = 1;
|
|
||||||
g_icons.lastMaintenanceFrame = -1;
|
g_icons.lastMaintenanceFrame = -1;
|
||||||
g_icons.lastPreviewBudgetFrame = -1;
|
g_icons.lastPreviewBudgetFrame = -1;
|
||||||
g_icons.previewLoadsThisFrame = 0;
|
g_icons.previewLoadsThisFrame = 0;
|
||||||
|
|||||||
@@ -13,7 +13,12 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_impl_dx12.h>
|
#include <imgui_impl_dx12.h>
|
||||||
#include <imgui_impl_win32.h>
|
#include <imgui_impl_win32.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <wrl/client.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#ifdef min
|
#ifdef min
|
||||||
@@ -32,6 +37,26 @@ namespace UI {
|
|||||||
|
|
||||||
class ImGuiBackendBridge {
|
class ImGuiBackendBridge {
|
||||||
public:
|
public:
|
||||||
|
struct UploadedTexture {
|
||||||
|
ImTextureID textureId = {};
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {};
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Resource> texture;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return textureId != ImTextureID{} && texture != nullptr && width > 0 && height > 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingTextureUpload {
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Resource> uploadResource;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList;
|
||||||
|
UINT64 fenceValue = 0;
|
||||||
|
};
|
||||||
|
|
||||||
static void EnableDpiAwareness() {
|
static void EnableDpiAwareness() {
|
||||||
ImGui_ImplWin32_EnableDpiAwareness();
|
ImGui_ImplWin32_EnableDpiAwareness();
|
||||||
}
|
}
|
||||||
@@ -83,6 +108,8 @@ public:
|
|||||||
m_srvDescriptorSize = 0;
|
m_srvDescriptorSize = 0;
|
||||||
m_srvCpuStart.ptr = 0;
|
m_srvCpuStart.ptr = 0;
|
||||||
m_srvGpuStart.ptr = 0;
|
m_srvGpuStart.ptr = 0;
|
||||||
|
m_uploadFence.Reset();
|
||||||
|
m_nextUploadFenceValue = 1;
|
||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +174,255 @@ public:
|
|||||||
FreeSrvDescriptorInternal(cpuHandle, gpuHandle);
|
FreeSrvDescriptorInternal(cpuHandle, gpuHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetUploadedTexture(UploadedTexture* texture) {
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture->cpuHandle.ptr != 0) {
|
||||||
|
FreeTextureDescriptor(texture->cpuHandle, texture->gpuHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
texture->texture.Reset();
|
||||||
|
texture->textureId = {};
|
||||||
|
texture->cpuHandle = {};
|
||||||
|
texture->gpuHandle = {};
|
||||||
|
texture->width = 0;
|
||||||
|
texture->height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UploadRgbaTexture(
|
||||||
|
ID3D12Device* device,
|
||||||
|
ID3D12CommandQueue* commandQueue,
|
||||||
|
const std::uint8_t* rgbaPixels,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
UploadedTexture* outTexture,
|
||||||
|
std::unique_ptr<PendingTextureUpload>* outPendingUpload) {
|
||||||
|
if (device == nullptr ||
|
||||||
|
commandQueue == nullptr ||
|
||||||
|
rgbaPixels == nullptr ||
|
||||||
|
width <= 0 ||
|
||||||
|
height <= 0 ||
|
||||||
|
outTexture == nullptr ||
|
||||||
|
outPendingUpload == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT srcRowPitch = static_cast<UINT>(width * 4);
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC textureDesc = {};
|
||||||
|
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||||
|
textureDesc.Alignment = 0;
|
||||||
|
textureDesc.Width = static_cast<UINT64>(width);
|
||||||
|
textureDesc.Height = static_cast<UINT>(height);
|
||||||
|
textureDesc.DepthOrArraySize = 1;
|
||||||
|
textureDesc.MipLevels = 1;
|
||||||
|
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
textureDesc.SampleDesc.Count = 1;
|
||||||
|
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES defaultHeap = {};
|
||||||
|
defaultHeap.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Resource> textureResource;
|
||||||
|
if (FAILED(device->CreateCommittedResource(
|
||||||
|
&defaultHeap,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
&textureDesc,
|
||||||
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&textureResource)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
|
||||||
|
UINT numRows = 0;
|
||||||
|
UINT64 rowSizeInBytes = 0;
|
||||||
|
UINT64 uploadBufferSize = 0;
|
||||||
|
device->GetCopyableFootprints(
|
||||||
|
&textureDesc,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
&footprint,
|
||||||
|
&numRows,
|
||||||
|
&rowSizeInBytes,
|
||||||
|
&uploadBufferSize);
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC uploadDesc = {};
|
||||||
|
uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
uploadDesc.Width = uploadBufferSize;
|
||||||
|
uploadDesc.Height = 1;
|
||||||
|
uploadDesc.DepthOrArraySize = 1;
|
||||||
|
uploadDesc.MipLevels = 1;
|
||||||
|
uploadDesc.SampleDesc.Count = 1;
|
||||||
|
uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES uploadHeap = {};
|
||||||
|
uploadHeap.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Resource> uploadResource;
|
||||||
|
if (FAILED(device->CreateCommittedResource(
|
||||||
|
&uploadHeap,
|
||||||
|
D3D12_HEAP_FLAG_NONE,
|
||||||
|
&uploadDesc,
|
||||||
|
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&uploadResource)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t* mappedData = nullptr;
|
||||||
|
if (FAILED(uploadResource->Map(0, nullptr, reinterpret_cast<void**>(&mappedData)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UINT row = 0; row < numRows; ++row) {
|
||||||
|
std::memcpy(
|
||||||
|
mappedData + footprint.Offset + static_cast<SIZE_T>(row) * footprint.Footprint.RowPitch,
|
||||||
|
rgbaPixels + static_cast<size_t>(row) * srcRowPitch,
|
||||||
|
srcRowPitch);
|
||||||
|
}
|
||||||
|
uploadResource->Unmap(0, nullptr);
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList;
|
||||||
|
if (FAILED(device->CreateCommandAllocator(
|
||||||
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
|
IID_PPV_ARGS(&commandAllocator)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FAILED(device->CreateCommandList(
|
||||||
|
0,
|
||||||
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
|
commandAllocator.Get(),
|
||||||
|
nullptr,
|
||||||
|
IID_PPV_ARGS(&commandList)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION dst = {};
|
||||||
|
dst.pResource = textureResource.Get();
|
||||||
|
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||||
|
dst.SubresourceIndex = 0;
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION src = {};
|
||||||
|
src.pResource = uploadResource.Get();
|
||||||
|
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||||
|
src.PlacedFootprint = footprint;
|
||||||
|
|
||||||
|
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier = {};
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Transition.pResource = textureResource.Get();
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
commandList->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
if (FAILED(commandList->Close())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnsureUploadFence(device)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UploadedTexture uploadedTexture = {};
|
||||||
|
AllocateTextureDescriptor(&uploadedTexture.cpuHandle, &uploadedTexture.gpuHandle);
|
||||||
|
if (uploadedTexture.cpuHandle.ptr == 0 || uploadedTexture.gpuHandle.ptr == 0) {
|
||||||
|
ResetUploadedTexture(&uploadedTexture);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||||
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
srvDesc.Texture2D.MipLevels = 1;
|
||||||
|
device->CreateShaderResourceView(textureResource.Get(), &srvDesc, uploadedTexture.cpuHandle);
|
||||||
|
|
||||||
|
uploadedTexture.texture = textureResource;
|
||||||
|
uploadedTexture.textureId = (ImTextureID)(static_cast<intptr_t>(uploadedTexture.gpuHandle.ptr));
|
||||||
|
uploadedTexture.width = width;
|
||||||
|
uploadedTexture.height = height;
|
||||||
|
|
||||||
|
ID3D12CommandList* commandLists[] = { commandList.Get() };
|
||||||
|
commandQueue->ExecuteCommandLists(1, commandLists);
|
||||||
|
const UINT64 fenceValue = m_nextUploadFenceValue++;
|
||||||
|
if (FAILED(commandQueue->Signal(m_uploadFence.Get(), fenceValue))) {
|
||||||
|
ResetUploadedTexture(&uploadedTexture);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pendingUpload = std::make_unique<PendingTextureUpload>();
|
||||||
|
pendingUpload->uploadResource = std::move(uploadResource);
|
||||||
|
pendingUpload->commandAllocator = std::move(commandAllocator);
|
||||||
|
pendingUpload->commandList = std::move(commandList);
|
||||||
|
pendingUpload->fenceValue = fenceValue;
|
||||||
|
|
||||||
|
*outTexture = std::move(uploadedTexture);
|
||||||
|
*outPendingUpload = std::move(pendingUpload);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTextureUploadComplete(const PendingTextureUpload& upload) const {
|
||||||
|
return m_uploadFence != nullptr && m_uploadFence->GetCompletedValue() >= upload.fenceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveCompletedTextureUploads(
|
||||||
|
std::vector<std::unique_ptr<PendingTextureUpload>>* uploads) const {
|
||||||
|
if (uploads == nullptr || m_uploadFence == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT64 completedFenceValue = m_uploadFence->GetCompletedValue();
|
||||||
|
uploads->erase(
|
||||||
|
std::remove_if(
|
||||||
|
uploads->begin(),
|
||||||
|
uploads->end(),
|
||||||
|
[completedFenceValue](const std::unique_ptr<PendingTextureUpload>& upload) {
|
||||||
|
return upload != nullptr && completedFenceValue >= upload->fenceValue;
|
||||||
|
}),
|
||||||
|
uploads->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaitForQueueIdle(ID3D12Device* device, ID3D12CommandQueue* commandQueue) const {
|
||||||
|
if (device == nullptr || commandQueue == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Fence> fence;
|
||||||
|
if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE eventHandle = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (eventHandle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr UINT64 kFenceValue = 1;
|
||||||
|
const HRESULT signalHr = commandQueue->Signal(fence.Get(), kFenceValue);
|
||||||
|
if (FAILED(signalHr)) {
|
||||||
|
CloseHandle(eventHandle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fence->GetCompletedValue() < kFenceValue) {
|
||||||
|
if (FAILED(fence->SetEventOnCompletion(kFenceValue, eventHandle))) {
|
||||||
|
CloseHandle(eventHandle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WaitForSingleObject(eventHandle, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(eventHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool HandleWindowMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
static bool HandleWindowMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam) != 0;
|
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam) != 0;
|
||||||
}
|
}
|
||||||
@@ -205,12 +481,22 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EnsureUploadFence(ID3D12Device* device) {
|
||||||
|
if (m_uploadFence) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_uploadFence)));
|
||||||
|
}
|
||||||
|
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
ID3D12DescriptorHeap* m_srvHeap = nullptr;
|
ID3D12DescriptorHeap* m_srvHeap = nullptr;
|
||||||
UINT m_srvDescriptorSize = 0;
|
UINT m_srvDescriptorSize = 0;
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE m_srvCpuStart = {};
|
D3D12_CPU_DESCRIPTOR_HANDLE m_srvCpuStart = {};
|
||||||
D3D12_GPU_DESCRIPTOR_HANDLE m_srvGpuStart = {};
|
D3D12_GPU_DESCRIPTOR_HANDLE m_srvGpuStart = {};
|
||||||
std::vector<bool> m_srvUsage;
|
std::vector<bool> m_srvUsage;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D12Fence> m_uploadFence;
|
||||||
|
UINT64 m_nextUploadFenceValue = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UI
|
} // namespace UI
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ set(EDITOR_TEST_SOURCES
|
|||||||
test_scene_viewport_picker.cpp
|
test_scene_viewport_picker.cpp
|
||||||
test_scene_viewport_overlay_renderer.cpp
|
test_scene_viewport_overlay_renderer.cpp
|
||||||
test_viewport_host_surface_utils.cpp
|
test_viewport_host_surface_utils.cpp
|
||||||
|
test_builtin_icon_layout_utils.cpp
|
||||||
${CMAKE_SOURCE_DIR}/editor/src/Core/UndoManager.cpp
|
${CMAKE_SOURCE_DIR}/editor/src/Core/UndoManager.cpp
|
||||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/SceneManager.cpp
|
${CMAKE_SOURCE_DIR}/editor/src/Managers/SceneManager.cpp
|
||||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/ProjectManager.cpp
|
${CMAKE_SOURCE_DIR}/editor/src/Managers/ProjectManager.cpp
|
||||||
|
|||||||
40
tests/editor/test_builtin_icon_layout_utils.cpp
Normal file
40
tests/editor/test_builtin_icon_layout_utils.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "UI/BuiltInIconLayoutUtils.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using XCEngine::Editor::UI::ComputeFittedIconSize;
|
||||||
|
|
||||||
|
TEST(BuiltInIconLayoutUtilsTest, ReturnsZeroWhenTextureOrBoundsAreInvalid) {
|
||||||
|
EXPECT_EQ(
|
||||||
|
ComputeFittedIconSize(0, 64, ImVec2(0.0f, 0.0f), ImVec2(64.0f, 64.0f)).x,
|
||||||
|
0.0f);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ComputeFittedIconSize(64, 64, ImVec2(10.0f, 0.0f), ImVec2(10.0f, 64.0f)).y,
|
||||||
|
0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltInIconLayoutUtilsTest, FitsWidthLimitedRegions) {
|
||||||
|
const ImVec2 size = ComputeFittedIconSize(
|
||||||
|
256,
|
||||||
|
128,
|
||||||
|
ImVec2(0.0f, 0.0f),
|
||||||
|
ImVec2(64.0f, 64.0f));
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(size.x, 64.0f);
|
||||||
|
EXPECT_FLOAT_EQ(size.y, 32.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BuiltInIconLayoutUtilsTest, FitsHeightLimitedRegions) {
|
||||||
|
const ImVec2 size = ComputeFittedIconSize(
|
||||||
|
128,
|
||||||
|
256,
|
||||||
|
ImVec2(0.0f, 0.0f),
|
||||||
|
ImVec2(80.0f, 40.0f));
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(size.x, 20.0f);
|
||||||
|
EXPECT_FLOAT_EQ(size.y, 40.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user