Files
XCEngine/tests/Rendering/unit/test_render_graph.cpp

629 lines
24 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Rendering/Graph/RenderGraphCompiler.h>
#include <XCEngine/Rendering/Graph/RenderGraphExecutor.h>
#include <memory>
#include <string>
#include <vector>
using namespace XCEngine::Rendering;
using namespace XCEngine::RHI;
namespace {
struct MockTransientAllocationState {
int createTextureCalls = 0;
int shutdownTextureCalls = 0;
int destroyTextureCalls = 0;
int createRenderTargetViewCalls = 0;
int shutdownRenderTargetViewCalls = 0;
int destroyRenderTargetViewCalls = 0;
int createShaderViewCalls = 0;
int shutdownShaderViewCalls = 0;
int destroyShaderViewCalls = 0;
};
class MockTransientTexture final : public RHITexture {
public:
MockTransientTexture(
std::shared_ptr<MockTransientAllocationState> state,
uint32_t width,
uint32_t height,
Format format)
: m_state(std::move(state))
, m_width(width)
, m_height(height)
, m_format(format) {
}
~MockTransientTexture() override {
++m_state->destroyTextureCalls;
}
uint32_t GetWidth() const override { return m_width; }
uint32_t GetHeight() const override { return m_height; }
uint32_t GetDepth() const override { return 1u; }
uint32_t GetMipLevels() const override { return 1u; }
Format GetFormat() const override { return m_format; }
TextureType GetTextureType() const override { return TextureType::Texture2D; }
ResourceStates GetState() const override { return m_stateValue; }
void SetState(ResourceStates state) override { m_stateValue = state; }
void* GetNativeHandle() override { return nullptr; }
const std::string& GetName() const override { return m_name; }
void SetName(const std::string& name) override { m_name = name; }
void Shutdown() override {
++m_state->shutdownTextureCalls;
}
private:
std::shared_ptr<MockTransientAllocationState> m_state;
uint32_t m_width = 0u;
uint32_t m_height = 0u;
Format m_format = Format::Unknown;
ResourceStates m_stateValue = ResourceStates::Common;
std::string m_name;
};
class MockTransientView final : public RHIResourceView {
public:
MockTransientView(
std::shared_ptr<MockTransientAllocationState> state,
ResourceViewType viewType,
Format format)
: m_state(std::move(state))
, m_viewType(viewType)
, m_format(format) {
}
~MockTransientView() override {
if (m_viewType == ResourceViewType::RenderTarget) {
++m_state->destroyRenderTargetViewCalls;
} else {
++m_state->destroyShaderViewCalls;
}
}
void Shutdown() override {
if (m_viewType == ResourceViewType::RenderTarget) {
++m_state->shutdownRenderTargetViewCalls;
} else {
++m_state->shutdownShaderViewCalls;
}
}
void* GetNativeHandle() override { return nullptr; }
bool IsValid() const override { return true; }
ResourceViewType GetViewType() const override { return m_viewType; }
ResourceViewDimension GetDimension() const override { return ResourceViewDimension::Texture2D; }
Format GetFormat() const override { return m_format; }
private:
std::shared_ptr<MockTransientAllocationState> m_state;
ResourceViewType m_viewType = ResourceViewType::ShaderResource;
Format m_format = Format::Unknown;
};
class MockImportedView final : public RHIResourceView {
public:
void Shutdown() override {}
void* GetNativeHandle() override { return nullptr; }
bool IsValid() const override { return true; }
ResourceViewType GetViewType() const override { return ResourceViewType::RenderTarget; }
ResourceViewDimension GetDimension() const override { return ResourceViewDimension::Texture2D; }
Format GetFormat() const override { return Format::R8G8B8A8_UNorm; }
};
class MockTransientDevice final : public RHIDevice {
public:
explicit MockTransientDevice(std::shared_ptr<MockTransientAllocationState> state)
: m_state(std::move(state)) {
}
bool Initialize(const RHIDeviceDesc&) override { return true; }
void Shutdown() override {}
RHIBuffer* CreateBuffer(const BufferDesc&) override { return nullptr; }
RHITexture* CreateTexture(const TextureDesc& desc) override {
++m_state->createTextureCalls;
return new MockTransientTexture(
m_state,
desc.width,
desc.height,
static_cast<Format>(desc.format));
}
RHITexture* CreateTexture(
const TextureDesc& desc,
const void*,
size_t,
uint32_t) override {
return CreateTexture(desc);
}
RHISwapChain* CreateSwapChain(const SwapChainDesc&, RHICommandQueue*) override { return nullptr; }
RHICommandList* CreateCommandList(const CommandListDesc&) override { return nullptr; }
RHICommandQueue* CreateCommandQueue(const CommandQueueDesc&) override { return nullptr; }
RHIShader* CreateShader(const ShaderCompileDesc&) override { return nullptr; }
RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc&) override { return nullptr; }
RHIPipelineLayout* CreatePipelineLayout(const RHIPipelineLayoutDesc&) override { return nullptr; }
RHIFence* CreateFence(const FenceDesc&) override { return nullptr; }
RHISampler* CreateSampler(const SamplerDesc&) override { return nullptr; }
RHIRenderPass* CreateRenderPass(uint32_t, const AttachmentDesc*, const AttachmentDesc*) override {
return nullptr;
}
RHIFramebuffer* CreateFramebuffer(
RHIRenderPass*,
uint32_t,
uint32_t,
uint32_t,
RHIResourceView**,
RHIResourceView*) override {
return nullptr;
}
RHIDescriptorPool* CreateDescriptorPool(const DescriptorPoolDesc&) override { return nullptr; }
RHIDescriptorSet* CreateDescriptorSet(RHIDescriptorPool*, const DescriptorSetLayoutDesc&) override {
return nullptr;
}
RHIResourceView* CreateVertexBufferView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateIndexBufferView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateRenderTargetView(
RHITexture*,
const ResourceViewDesc& desc) override {
++m_state->createRenderTargetViewCalls;
return new MockTransientView(
m_state,
ResourceViewType::RenderTarget,
static_cast<Format>(desc.format));
}
RHIResourceView* CreateDepthStencilView(RHITexture*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateShaderResourceView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateShaderResourceView(
RHITexture*,
const ResourceViewDesc& desc) override {
++m_state->createShaderViewCalls;
return new MockTransientView(
m_state,
ResourceViewType::ShaderResource,
static_cast<Format>(desc.format));
}
RHIResourceView* CreateUnorderedAccessView(RHIBuffer*, const ResourceViewDesc&) override { return nullptr; }
RHIResourceView* CreateUnorderedAccessView(RHITexture*, const ResourceViewDesc&) override {
return nullptr;
}
const RHICapabilities& GetCapabilities() const override { return m_capabilities; }
const RHIDeviceInfo& GetDeviceInfo() const override { return m_deviceInfo; }
void* GetNativeDevice() override { return nullptr; }
private:
std::shared_ptr<MockTransientAllocationState> m_state;
RHICapabilities m_capabilities = {};
RHIDeviceInfo m_deviceInfo = {};
};
struct MockTransitionBarrierCall {
RHIResourceView* resource = nullptr;
ResourceStates before = ResourceStates::Common;
ResourceStates after = ResourceStates::Common;
};
class MockTransientCommandList final : public RHICommandList {
public:
void Shutdown() override {}
void Reset() override {}
void Close() override {}
void TransitionBarrier(
RHIResourceView* resource,
ResourceStates stateBefore,
ResourceStates stateAfter) override {
MockTransitionBarrierCall call = {};
call.resource = resource;
call.before = stateBefore;
call.after = stateAfter;
transitionCalls.push_back(call);
}
void BeginRenderPass(
RHIRenderPass*,
RHIFramebuffer*,
const Rect&,
uint32_t,
const ClearValue*) override {
}
void EndRenderPass() override {}
void SetShader(RHIShader*) override {}
void SetPipelineState(RHIPipelineState*) override {}
void SetGraphicsDescriptorSets(uint32_t, uint32_t, RHIDescriptorSet**, RHIPipelineLayout*) override {}
void SetComputeDescriptorSets(uint32_t, uint32_t, RHIDescriptorSet**, RHIPipelineLayout*) override {}
void SetPrimitiveTopology(PrimitiveTopology) override {}
void SetViewport(const Viewport&) override {}
void SetViewports(uint32_t, const Viewport*) override {}
void SetScissorRect(const Rect&) override {}
void SetScissorRects(uint32_t, const Rect*) override {}
void SetRenderTargets(uint32_t, RHIResourceView**, RHIResourceView*) override {}
void SetStencilRef(uint8_t) override {}
void SetBlendFactor(const float[4]) override {}
void SetVertexBuffers(uint32_t, uint32_t, RHIResourceView**, const uint64_t*, const uint32_t*) override {}
void SetIndexBuffer(RHIResourceView*, uint64_t) override {}
void Draw(uint32_t, uint32_t, uint32_t, uint32_t) override {}
void DrawIndexed(uint32_t, uint32_t, uint32_t, int32_t, uint32_t) override {}
void Clear(float, float, float, float, uint32_t) override {}
void ClearRenderTarget(RHIResourceView*, const float[4], uint32_t, const Rect*) override {}
void ClearDepthStencil(RHIResourceView*, float, uint8_t, uint32_t, const Rect*) override {}
void CopyResource(RHIResourceView*, RHIResourceView*) override {}
void Dispatch(uint32_t, uint32_t, uint32_t) override {}
std::vector<MockTransitionBarrierCall> transitionCalls = {};
};
RenderGraphTextureDesc BuildTestTextureDesc() {
RenderGraphTextureDesc desc = {};
desc.width = 1280u;
desc.height = 720u;
desc.format = static_cast<XCEngine::Core::uint32>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<XCEngine::Core::uint32>(TextureType::Texture2D);
desc.sampleCount = 1u;
desc.sampleQuality = 0u;
return desc;
}
} // namespace
TEST(RenderGraph_Test, CompilesDeclaredPassOrderAndTracksTextureLifetimes) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
const RenderGraphTextureHandle sceneColor = builder.CreateTransientTexture("SceneColor", desc);
const RenderGraphTextureHandle bloomColor = builder.CreateTransientTexture("BloomColor", desc);
const RenderGraphTextureHandle backBuffer = builder.ImportTexture("BackBuffer", desc);
builder.AddRasterPass(
"Opaque",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(sceneColor);
});
builder.AddComputePass(
"Bloom",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(sceneColor);
pass.WriteTexture(bloomColor);
});
builder.AddRasterPass(
"Final",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(bloomColor);
pass.WriteTexture(backBuffer);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(compiledGraph.GetPassCount(), 3u);
EXPECT_EQ(compiledGraph.GetPassName(0u), "Opaque");
EXPECT_EQ(compiledGraph.GetPassName(1u), "Bloom");
EXPECT_EQ(compiledGraph.GetPassName(2u), "Final");
EXPECT_EQ(compiledGraph.GetPassType(0u), RenderGraphPassType::Raster);
EXPECT_EQ(compiledGraph.GetPassType(1u), RenderGraphPassType::Compute);
RenderGraphTextureLifetime lifetime = {};
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(sceneColor, lifetime));
EXPECT_TRUE(lifetime.used);
EXPECT_EQ(lifetime.kind, RenderGraphTextureKind::Transient);
EXPECT_EQ(lifetime.firstPassIndex, 0u);
EXPECT_EQ(lifetime.lastPassIndex, 1u);
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(bloomColor, lifetime));
EXPECT_TRUE(lifetime.used);
EXPECT_EQ(lifetime.firstPassIndex, 1u);
EXPECT_EQ(lifetime.lastPassIndex, 2u);
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(backBuffer, lifetime));
EXPECT_TRUE(lifetime.used);
EXPECT_EQ(lifetime.kind, RenderGraphTextureKind::Imported);
EXPECT_EQ(lifetime.firstPassIndex, 2u);
EXPECT_EQ(lifetime.lastPassIndex, 2u);
}
TEST(RenderGraph_Test, OrdersImportedTextureHazardsAcrossFullscreenStyleChain) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
const RenderGraphTextureHandle sceneColor = builder.ImportTexture(
"SceneColor",
desc,
reinterpret_cast<RHIResourceView*>(1));
const RenderGraphTextureHandle postColor = builder.ImportTexture(
"PostColor",
desc,
reinterpret_cast<RHIResourceView*>(2));
builder.AddRasterPass(
"MainScene",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(sceneColor);
});
builder.AddRasterPass(
"PostProcess",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(sceneColor);
pass.WriteTexture(postColor);
});
builder.AddRasterPass(
"FinalOutput",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(postColor);
pass.WriteTexture(sceneColor);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(compiledGraph.GetPassCount(), 3u);
EXPECT_EQ(compiledGraph.GetPassName(0u), "MainScene");
EXPECT_EQ(compiledGraph.GetPassName(1u), "PostProcess");
EXPECT_EQ(compiledGraph.GetPassName(2u), "FinalOutput");
RenderGraphTextureLifetime lifetime = {};
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(sceneColor, lifetime));
EXPECT_EQ(lifetime.kind, RenderGraphTextureKind::Imported);
EXPECT_EQ(lifetime.firstPassIndex, 0u);
EXPECT_EQ(lifetime.lastPassIndex, 2u);
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(postColor, lifetime));
EXPECT_EQ(lifetime.kind, RenderGraphTextureKind::Imported);
EXPECT_EQ(lifetime.firstPassIndex, 1u);
EXPECT_EQ(lifetime.lastPassIndex, 2u);
}
TEST(RenderGraph_Test, PreservesImportedTextureStateContractAcrossCompile) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
RenderGraphImportedTextureOptions importedOptions = {};
importedOptions.initialState = ResourceStates::Present;
importedOptions.finalState = ResourceStates::PixelShaderResource;
importedOptions.graphOwnsTransitions = true;
const RenderGraphTextureHandle importedTexture = builder.ImportTexture(
"ImportedColor",
desc,
reinterpret_cast<RHIResourceView*>(7),
importedOptions);
builder.AddRasterPass(
"SampleImported",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(importedTexture);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
RenderGraphImportedTextureOptions resolvedOptions = {};
ASSERT_TRUE(compiledGraph.TryGetImportedTextureOptions(importedTexture, resolvedOptions));
EXPECT_EQ(resolvedOptions.initialState, ResourceStates::Present);
EXPECT_EQ(resolvedOptions.finalState, ResourceStates::PixelShaderResource);
EXPECT_TRUE(resolvedOptions.graphOwnsTransitions);
RenderGraphTextureTransitionPlan transitionPlan = {};
ASSERT_TRUE(compiledGraph.TryGetTextureTransitionPlan(importedTexture, transitionPlan));
EXPECT_TRUE(transitionPlan.graphOwnsTransitions);
EXPECT_TRUE(transitionPlan.hasFirstAccessState);
EXPECT_TRUE(transitionPlan.hasLastAccessState);
EXPECT_EQ(transitionPlan.initialState, ResourceStates::Present);
EXPECT_EQ(transitionPlan.firstAccessState, ResourceStates::PixelShaderResource);
EXPECT_EQ(transitionPlan.lastAccessState, ResourceStates::PixelShaderResource);
EXPECT_EQ(transitionPlan.finalState, ResourceStates::PixelShaderResource);
}
TEST(RenderGraph_Test, ExecutesGraphOwnedImportedTextureTransitionsAtGraphBoundaries) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
RenderGraphImportedTextureOptions importedOptions = {};
importedOptions.initialState = ResourceStates::Present;
importedOptions.finalState = ResourceStates::Present;
importedOptions.graphOwnsTransitions = true;
MockImportedView importedView;
const RenderGraphTextureHandle backBuffer = builder.ImportTexture(
"BackBuffer",
desc,
&importedView,
importedOptions);
builder.AddRasterPass(
"FinalBlit",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(backBuffer);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
MockTransientCommandList commandList;
RenderContext renderContext = {};
renderContext.device = reinterpret_cast<RHIDevice*>(1);
renderContext.commandList = &commandList;
renderContext.commandQueue = reinterpret_cast<RHICommandQueue*>(1);
ASSERT_TRUE(RenderGraphExecutor::Execute(compiledGraph, renderContext, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(commandList.transitionCalls.size(), 2u);
EXPECT_EQ(commandList.transitionCalls[0].resource, &importedView);
EXPECT_EQ(commandList.transitionCalls[0].before, ResourceStates::Present);
EXPECT_EQ(commandList.transitionCalls[0].after, ResourceStates::RenderTarget);
EXPECT_EQ(commandList.transitionCalls[1].resource, &importedView);
EXPECT_EQ(commandList.transitionCalls[1].before, ResourceStates::RenderTarget);
EXPECT_EQ(commandList.transitionCalls[1].after, ResourceStates::Present);
}
TEST(RenderGraph_Test, RejectsTransientTextureReadBeforeWrite) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
const RenderGraphTextureHandle sceneColor = builder.CreateTransientTexture("SceneColor", desc);
builder.AddRasterPass(
"InvalidRead",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(sceneColor);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
EXPECT_FALSE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage));
EXPECT_FALSE(errorMessage.Empty());
}
TEST(RenderGraph_Test, RejectsGraphOwnedImportedTextureWithoutView) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
RenderGraphImportedTextureOptions importedOptions = {};
importedOptions.initialState = ResourceStates::Present;
importedOptions.finalState = ResourceStates::Present;
importedOptions.graphOwnsTransitions = true;
const RenderGraphTextureHandle importedTexture = builder.ImportTexture(
"BackBuffer",
desc,
nullptr,
importedOptions);
builder.AddRasterPass(
"FinalBlit",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(importedTexture);
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
EXPECT_FALSE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage));
EXPECT_FALSE(errorMessage.Empty());
}
TEST(RenderGraph_Test, ExecutesCompiledPassCallbacksInCompiledOrder) {
RenderGraph graph;
RenderGraphBuilder builder(graph);
const RenderGraphTextureDesc desc = BuildTestTextureDesc();
const RenderGraphTextureHandle sceneColor = builder.CreateTransientTexture("SceneColor", desc);
const RenderGraphTextureHandle backBuffer = builder.ImportTexture(
"BackBuffer",
desc,
reinterpret_cast<RHIResourceView*>(1));
std::vector<std::string> eventLog;
auto allocationState = std::make_shared<MockTransientAllocationState>();
MockTransientDevice device(allocationState);
MockTransientCommandList commandList;
RHIResourceView* opaqueTargetView = nullptr;
RHIResourceView* finalSourceView = nullptr;
builder.AddRasterPass(
"Opaque",
[&](RenderGraphPassBuilder& pass) {
pass.WriteTexture(sceneColor);
pass.SetExecuteCallback(
[&](const RenderGraphExecutionContext& executionContext) {
EXPECT_TRUE(executionContext.IsTransientTexture(sceneColor));
EXPECT_NE(
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::RenderTarget),
nullptr);
opaqueTargetView =
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::RenderTarget);
eventLog.push_back("Opaque");
});
});
builder.AddRasterPass(
"Final",
[&](RenderGraphPassBuilder& pass) {
pass.ReadTexture(sceneColor);
pass.WriteTexture(backBuffer);
pass.SetExecuteCallback(
[&](const RenderGraphExecutionContext& executionContext) {
RenderGraphTextureDesc resolvedDesc = {};
EXPECT_TRUE(executionContext.TryGetTextureDesc(sceneColor, resolvedDesc));
EXPECT_EQ(resolvedDesc.width, desc.width);
EXPECT_NE(
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::ShaderResource),
nullptr);
finalSourceView =
executionContext.ResolveTextureView(
sceneColor,
RenderGraphTextureViewType::ShaderResource);
eventLog.push_back("Final");
});
});
CompiledRenderGraph compiledGraph;
XCEngine::Containers::String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
RenderContext renderContext = {};
renderContext.device = &device;
renderContext.commandList = &commandList;
renderContext.commandQueue = reinterpret_cast<RHICommandQueue*>(1);
ASSERT_TRUE(RenderGraphExecutor::Execute(compiledGraph, renderContext, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(eventLog.size(), 2u);
EXPECT_EQ(eventLog[0], "Opaque");
EXPECT_EQ(eventLog[1], "Final");
ASSERT_EQ(commandList.transitionCalls.size(), 2u);
EXPECT_EQ(commandList.transitionCalls[0].resource, opaqueTargetView);
EXPECT_EQ(commandList.transitionCalls[0].before, ResourceStates::Common);
EXPECT_EQ(commandList.transitionCalls[0].after, ResourceStates::RenderTarget);
EXPECT_EQ(commandList.transitionCalls[1].resource, finalSourceView);
EXPECT_EQ(commandList.transitionCalls[1].before, ResourceStates::RenderTarget);
EXPECT_EQ(commandList.transitionCalls[1].after, ResourceStates::PixelShaderResource);
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
EXPECT_EQ(allocationState->shutdownRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1);
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
EXPECT_EQ(allocationState->destroyRenderTargetViewCalls, 1);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 1);
}