1f9b284de2
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h # Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h # Code/Editor/Plugins/EditorCommon/stdafx.cpp # Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h # Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h # Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h # Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h # Code/Tools/AssetProcessor/native/precompiled.h # Code/Tools/Standalone/StandaloneTools_precompiled.h # Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h # Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h # Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h # Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h # Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h # Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h # Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake # Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h # Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h # Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h # Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp # Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake # Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h # Gems/Blast/Code/Source/StdAfx.cpp # Gems/Camera/Code/Source/Camera_precompiled.h # Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h # Gems/FastNoise/Code/Source/FastNoise_precompiled.h # Gems/Gestures/Code/Source/Gestures_precompiled.h # Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h # Gems/GraphCanvas/Code/precompiled.h # Gems/ImGui/Code/Source/ImGui_precompiled.h # Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h # Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h # Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp # Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h # Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h # Gems/LyShine/Code/Source/LyShine_precompiled.h # Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h # Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h # Gems/Maestro/Code/Source/Maestro_precompiled.h # Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h # Gems/Metastream/Code/Source/Metastream_precompiled.h # Gems/Microphone/Code/Source/Microphone_precompiled.h # Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h # Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h # Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h # Gems/PhysX/Code/Source/PhysX_precompiled.h # Gems/PhysX/Code/physx_unsupported_files.cmake # Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h # Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h # Gems/ScriptCanvas/Code/Editor/precompiled.h # Gems/ScriptCanvas/Code/Source/precompiled.h # Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h # Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h # Gems/ScriptEvents/Code/Source/precompiled.h # Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp # Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h # Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h # Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h # Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h # Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h # Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h # Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h # Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h # Gems/Twitch/Code/Source/Twitch_precompiled.h # Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h # Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h # Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
366 lines
14 KiB
C++
366 lines
14 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include "RHITestFixture.h"
|
|
#include <Tests/Factory.h>
|
|
#include <Tests/Device.h>
|
|
#include <Tests/ThreadTester.h>
|
|
|
|
#include <Atom/RHI/PipelineStateCache.h>
|
|
|
|
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
|
|
|
#include <AzCore/Math/Random.h>
|
|
|
|
namespace UnitTest
|
|
{
|
|
using namespace AZ;
|
|
|
|
class PipelineStateTests
|
|
: public RHITestFixture
|
|
{
|
|
static const uint32_t s_heapSizeMB = 64;
|
|
|
|
protected:
|
|
void ScrambleMemory(void* memory, size_t size, uint32_t seed)
|
|
{
|
|
SimpleLcgRandom random(seed);
|
|
|
|
uint8_t* bytes = reinterpret_cast<uint8_t*>(memory);
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
bytes[i] = static_cast<uint8_t>(random.GetRandom());
|
|
}
|
|
}
|
|
|
|
// Generates random render state. Everything else is left empty or default as much as possible,
|
|
// but we do touch-up the data to make sure we don't end up with something that will fail assertions.
|
|
// The point here is to create a unique descriptor that will have a unique hash value.
|
|
RHI::PipelineStateDescriptorForDraw CreatePipelineStateDescriptor(uint32_t randomSeed)
|
|
{
|
|
RHI::PipelineStateDescriptorForDraw desc;
|
|
desc.m_inputStreamLayout.SetTopology(RHI::PrimitiveTopology::TriangleList);
|
|
desc.m_inputStreamLayout.Finalize();
|
|
desc.m_pipelineLayoutDescriptor = m_pipelineLayout;
|
|
|
|
ScrambleMemory(&desc.m_renderStates, sizeof(RHI::RenderStates), randomSeed++);
|
|
|
|
desc.m_renderStates.m_depthStencilState.m_depth.m_enable = true;
|
|
auto& renderAttachmentLayout = desc.m_renderAttachmentConfiguration.m_renderAttachmentLayout;
|
|
renderAttachmentLayout.m_attachmentCount = 2;
|
|
renderAttachmentLayout.m_attachmentFormats[0] = RHI::Format::R32_FLOAT;
|
|
renderAttachmentLayout.m_attachmentFormats[1] = RHI::Format::R8G8B8A8_SNORM;
|
|
renderAttachmentLayout.m_subpassCount = 1;
|
|
renderAttachmentLayout.m_subpassLayouts[0].m_rendertargetCount = 1;
|
|
renderAttachmentLayout.m_subpassLayouts[0].m_rendertargetDescriptors[0] = RHI::RenderAttachmentDescriptor{ 1, RHI::InvalidRenderAttachmentIndex, RHI::AttachmentLoadStoreAction() };
|
|
renderAttachmentLayout.m_subpassLayouts[0].m_depthStencilDescriptor = RHI::RenderAttachmentDescriptor{ 0, RHI::InvalidRenderAttachmentIndex, RHI::AttachmentLoadStoreAction() };
|
|
desc.m_renderAttachmentConfiguration.m_subpassIndex = 0;
|
|
return desc;
|
|
}
|
|
|
|
void ValidateCacheIntegrity(RHI::Ptr<RHI::PipelineStateCache>& cache) const
|
|
{
|
|
cache->ValidateCacheIntegrity();
|
|
}
|
|
|
|
private:
|
|
|
|
void SetUp() override
|
|
{
|
|
RHITestFixture::SetUp();
|
|
m_factory.reset(aznew Factory());
|
|
|
|
m_pipelineLayout = RHI::PipelineLayoutDescriptor::Create();
|
|
m_pipelineLayout->Finalize();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_pipelineLayout = nullptr;
|
|
|
|
m_factory.reset();
|
|
RHITestFixture::TearDown();
|
|
}
|
|
|
|
RHI::Ptr<RHI::PipelineLayoutDescriptor> m_pipelineLayout;
|
|
AZStd::unique_ptr<Factory> m_factory;
|
|
};
|
|
|
|
TEST_F(PipelineStateTests, PipelineState_CreateEmpty_Test)
|
|
{
|
|
RHI::Ptr<RHI::PipelineState> empty = RHI::Factory::Get().CreatePipelineState();
|
|
EXPECT_NE(empty.get(), nullptr);
|
|
EXPECT_FALSE(empty->IsInitialized());
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineState_Init_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
|
|
RHI::Ptr<RHI::PipelineState> pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
RHI::ResultCode resultCode = pipelineState->Init(*device, CreatePipelineStateDescriptor(0));
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::Success);
|
|
|
|
// Second init should fail and throw validation error.
|
|
AZ_TEST_START_ASSERTTEST;
|
|
resultCode = pipelineState->Init(*device, CreatePipelineStateDescriptor(0));
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineState_Init_Subpass)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
|
|
RHI::Ptr<RHI::PipelineState> pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
auto descriptor = CreatePipelineStateDescriptor(0);
|
|
descriptor.m_renderAttachmentConfiguration.m_subpassIndex = 1337;
|
|
AZ_TEST_START_ASSERTTEST;
|
|
RHI::ResultCode resultCode = pipelineState->Init(*device, descriptor);
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineState_Init_SubpassInput)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
|
|
RHI::Ptr<RHI::PipelineState> pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
auto descriptor = CreatePipelineStateDescriptor(0);
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_subpassInputCount = 1;
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_subpassInputIndices[0] = 1;
|
|
RHI::ResultCode resultCode = pipelineState->Init(*device, descriptor);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::Success);
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_subpassInputIndices[0] = 3;
|
|
resultCode = pipelineState->Init(*device, descriptor);
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineState_Init_Resolve)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
|
|
RHI::Ptr<RHI::PipelineState> pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
auto descriptor = CreatePipelineStateDescriptor(0);
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_rendertargetDescriptors[0].m_resolveAttachmentIndex = 1;
|
|
RHI::ResultCode resultCode = pipelineState->Init(*device, descriptor);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::Success);
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_rendertargetDescriptors[0].m_resolveAttachmentIndex = 5;
|
|
resultCode = pipelineState->Init(*device, descriptor);
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
pipelineState = RHI::Factory::Get().CreatePipelineState();
|
|
descriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout.m_subpassLayouts[0].m_rendertargetDescriptors[0].m_resolveAttachmentIndex = 0;
|
|
resultCode = pipelineState->Init(*device, descriptor);
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineLibrary_CreateEmpty_Test)
|
|
{
|
|
RHI::Ptr<RHI::PipelineLibrary> empty = RHI::Factory::Get().CreatePipelineLibrary();
|
|
EXPECT_NE(empty.get(), nullptr);
|
|
EXPECT_FALSE(empty->IsInitialized());
|
|
|
|
AZ_TEST_START_ASSERTTEST;
|
|
EXPECT_EQ(empty->MergeInto({}), RHI::ResultCode::InvalidOperation);
|
|
EXPECT_EQ(empty->GetSerializedData(), nullptr);
|
|
AZ_TEST_STOP_ASSERTTEST(2);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineLibrary_Init_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
|
|
RHI::Ptr<RHI::PipelineLibrary> pipelineLibrary = RHI::Factory::Get().CreatePipelineLibrary();
|
|
RHI::ResultCode resultCode = pipelineLibrary->Init(*device, nullptr);
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::Success);
|
|
|
|
// Second init should fail and throw validation error.
|
|
AZ_TEST_START_ASSERTTEST;
|
|
resultCode = pipelineLibrary->Init(*device, nullptr);
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
|
|
EXPECT_EQ(resultCode, RHI::ResultCode::InvalidOperation);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineStateCache_Init_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
RHI::Ptr<RHI::PipelineStateCache> pipelineStateCache = RHI::PipelineStateCache::Create(*device);
|
|
|
|
AZStd::array<RHI::PipelineLibraryHandle, RHI::PipelineStateCache::LibraryCountMax> handles;
|
|
for (size_t i = 0; i < handles.size(); ++i)
|
|
{
|
|
handles[i] = pipelineStateCache->CreateLibrary(nullptr);
|
|
|
|
EXPECT_TRUE(handles[i].IsValid());
|
|
|
|
for (size_t j = 0; j < i; ++j)
|
|
{
|
|
EXPECT_NE(handles[i], handles[j]);
|
|
}
|
|
}
|
|
|
|
// Creating more than the maximum number of libraries should assert but still function.
|
|
AZ_TEST_START_ASSERTTEST;
|
|
EXPECT_EQ(pipelineStateCache->CreateLibrary(nullptr), RHI::PipelineLibraryHandle{});
|
|
AZ_TEST_STOP_ASSERTTEST(1);
|
|
|
|
// Reset should no-op.
|
|
pipelineStateCache->Reset();
|
|
|
|
for (size_t i = 0; i < handles.size(); ++i)
|
|
{
|
|
pipelineStateCache->ResetLibrary(handles[i]);
|
|
pipelineStateCache->ReleaseLibrary(handles[i]);
|
|
}
|
|
|
|
// Test free-list by allocating another set of libraries.
|
|
|
|
for (size_t i = 0; i < handles.size(); ++i)
|
|
{
|
|
handles[i] = pipelineStateCache->CreateLibrary(nullptr);
|
|
EXPECT_FALSE(handles[i].IsNull());
|
|
}
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineStateCache_NullHandle_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
RHI::Ptr<RHI::PipelineStateCache> pipelineStateCache = RHI::PipelineStateCache::Create(*device);
|
|
|
|
// Calling library methods with a null handle should early out.
|
|
pipelineStateCache->ResetLibrary({});
|
|
pipelineStateCache->ReleaseLibrary({});
|
|
EXPECT_EQ(pipelineStateCache->GetLibrarySerializedData({}), nullptr);
|
|
EXPECT_EQ(pipelineStateCache->AcquirePipelineState({}, CreatePipelineStateDescriptor(0)), nullptr);
|
|
pipelineStateCache->Compact();
|
|
ValidateCacheIntegrity(pipelineStateCache);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineStateCache_PipelineStateThreading_Same_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
RHI::Ptr<RHI::PipelineStateCache> pipelineStateCache = RHI::PipelineStateCache::Create(*device);
|
|
|
|
static const size_t IterationCountMax = 10000;
|
|
static const size_t ThreadCountMax = 8;
|
|
|
|
RHI::PipelineStateDescriptorForDraw descriptor = CreatePipelineStateDescriptor(0);
|
|
|
|
RHI::PipelineLibraryHandle libraryHandle = pipelineStateCache->CreateLibrary(nullptr);
|
|
|
|
AZStd::mutex mutex;
|
|
AZStd::unordered_set<const RHI::PipelineState*> pipelineStatesMerged;
|
|
|
|
ThreadTester::Dispatch(ThreadCountMax, [&] ([[maybe_unused]] size_t threadIndex)
|
|
{
|
|
AZStd::unordered_set<const RHI::PipelineState*> pipelineStates;
|
|
|
|
for (size_t i = 0; i < IterationCountMax; ++i)
|
|
{
|
|
pipelineStates.insert(pipelineStateCache->AcquirePipelineState(libraryHandle, descriptor));
|
|
}
|
|
|
|
EXPECT_EQ(pipelineStates.size(), 1);
|
|
EXPECT_NE(*pipelineStates.begin(), nullptr);
|
|
|
|
mutex.lock();
|
|
pipelineStatesMerged.insert(*pipelineStates.begin());
|
|
mutex.unlock();
|
|
});
|
|
|
|
pipelineStateCache->Compact();
|
|
ValidateCacheIntegrity(pipelineStateCache);
|
|
|
|
EXPECT_EQ(pipelineStatesMerged.size(), 1);
|
|
}
|
|
|
|
TEST_F(PipelineStateTests, PipelineStateCache_PipelineStateThreading_Fuzz_Test)
|
|
{
|
|
RHI::Ptr<RHI::Device> device = MakeTestDevice();
|
|
RHI::Ptr<RHI::PipelineStateCache> pipelineStateCache = RHI::PipelineStateCache::Create(*device);
|
|
|
|
static const size_t CycleIterationCountMax = 4;
|
|
static const size_t AcquireIterationCountMax = 2000;
|
|
static const size_t ThreadCountMax = 4;
|
|
static const size_t PipelineStateCountMax = 128;
|
|
static const size_t LibraryCountMax = 2;
|
|
|
|
AZStd::vector<RHI::PipelineStateDescriptorForDraw> descriptors;
|
|
descriptors.reserve(PipelineStateCountMax);
|
|
for (size_t i = 0; i < PipelineStateCountMax; ++i)
|
|
{
|
|
descriptors.push_back(CreatePipelineStateDescriptor(static_cast<uint32_t>(i)));
|
|
}
|
|
|
|
AZStd::vector<RHI::PipelineLibraryHandle> libraryHandles;
|
|
for (size_t i = 0; i < LibraryCountMax; ++i)
|
|
{
|
|
libraryHandles.push_back(pipelineStateCache->CreateLibrary(nullptr));
|
|
}
|
|
|
|
AZStd::mutex mutex;
|
|
|
|
for (size_t cycleIndex = 0; cycleIndex < CycleIterationCountMax; ++cycleIndex)
|
|
{
|
|
for (size_t libraryIndex = 0; libraryIndex < LibraryCountMax; ++libraryIndex)
|
|
{
|
|
RHI::PipelineLibraryHandle libraryHandle = libraryHandles[libraryIndex];
|
|
|
|
AZStd::unordered_set<const RHI::PipelineState*> pipelineStatesMerged;
|
|
|
|
ThreadTester::Dispatch(ThreadCountMax, [&](size_t threadIndex)
|
|
{
|
|
SimpleLcgRandom random(threadIndex);
|
|
|
|
AZStd::unordered_set<const RHI::PipelineState*> pipelineStates;
|
|
|
|
for (size_t i = 0; i < AcquireIterationCountMax; ++i)
|
|
{
|
|
size_t descriptorIndex = random.GetRandom() % descriptors.size();
|
|
|
|
pipelineStates.emplace(pipelineStateCache->AcquirePipelineState(libraryHandle, descriptors[descriptorIndex]));
|
|
}
|
|
|
|
mutex.lock();
|
|
for (const RHI::PipelineState* pipelineState : pipelineStates)
|
|
{
|
|
pipelineStatesMerged.emplace(pipelineState);
|
|
}
|
|
mutex.unlock();
|
|
});
|
|
|
|
EXPECT_EQ(pipelineStatesMerged.size(), PipelineStateCountMax);
|
|
}
|
|
|
|
pipelineStateCache->Compact();
|
|
ValidateCacheIntegrity(pipelineStateCache);
|
|
|
|
// Halfway through, reset the caches.
|
|
|
|
if (cycleIndex == (CycleIterationCountMax / 2))
|
|
{
|
|
pipelineStateCache->Reset();
|
|
}
|
|
}
|
|
}
|
|
}
|