Merge remote-tracking branch 'upstream/development' into compiletime_filerequest_code2
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
#include <AssetBuilderInfo.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <Entity/EntityUtilityComponent.h>
|
||||
#include <AssetBuilderStatic.h>
|
||||
|
||||
namespace AssetBuilder
|
||||
{
|
||||
@@ -165,6 +166,7 @@ void AssetBuilderApplication::StartCommon(AZ::Entity* systemEntity)
|
||||
|
||||
AssetBuilderSDK::InitializeSerializationContext();
|
||||
AssetBuilderSDK::InitializeBehaviorContext();
|
||||
AssetBuilder::InitializeSerializationContext();
|
||||
// the asset builder app never writes source files, only assets, so there is no need to do any kind of asset upgrading
|
||||
AZ::Data::AssetManager::Instance().SetAssetInfoUpgradingEnabled(false);
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzFramework/Asset/AssetSystemComponent.h>
|
||||
#include <ToolsComponents/ToolsAssetCatalogComponent.h>
|
||||
#include <AssetBuilderStatic.h>
|
||||
|
||||
// Command-line parameter options:
|
||||
static const char* const s_paramHelp = "help"; // Print help information.
|
||||
@@ -52,10 +53,10 @@ static const char* const s_paramDebugCreate = "debug_create"; // Debug mode for
|
||||
static const char* const s_paramDebugProcess = "debug_process"; // Debug mode for the process job of the specified file.
|
||||
static const char* const s_paramPlatformTags = "tags"; // Additional list of tags to add platform tag list.
|
||||
static const char* const s_paramPlatform = "platform"; // Platform to use
|
||||
static const char* const s_paramRegisterBuilders = "register"; // Indicates the AP is starting up and requesting a list of registered builders
|
||||
|
||||
// Task modes:
|
||||
static const char* const s_taskResident = "resident"; // stays up and running indefinitely, accepting jobs via network connection
|
||||
static const char* const s_taskRegisterBuilder = "register"; // outputs all the builder descriptors
|
||||
static const char* const s_taskCreateJob = "create"; // runs a builders createJobs function
|
||||
static const char* const s_taskProcessJob = "process"; // runs processJob function
|
||||
static const char* const s_taskDebug = "debug"; // runs a one shot job in a fake environment for a specified file.
|
||||
@@ -205,6 +206,40 @@ void AssetBuilderComponent::Reflect(AZ::ReflectContext* context)
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetBuilderComponent::DoHelloPing()
|
||||
{
|
||||
using namespace AssetBuilder;
|
||||
|
||||
BuilderHelloRequest request;
|
||||
BuilderHelloResponse response;
|
||||
|
||||
AZStd::string id;
|
||||
|
||||
if (!GetParameter(s_paramId, id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
request.m_uuid = AZ::Uuid::CreateString(id.c_str());
|
||||
|
||||
AZ_TracePrintf(
|
||||
"AssetBuilderComponent", "RunInResidentMode: Pinging asset processor with the builder UUID %s\n",
|
||||
request.m_uuid.ToString<AZStd::string>().c_str());
|
||||
|
||||
bool result = AzFramework::AssetSystem::SendRequest(request, response);
|
||||
|
||||
AZ_Error("AssetBuilder", result, "Failed to send hello request to Asset Processor");
|
||||
// This error is only shown if we successfully got a response AND the response explicitly indicates the AP rejected the builder
|
||||
AZ_Error("AssetBuilder", !result || response.m_accepted, "Asset Processor rejected connection request");
|
||||
|
||||
if (result)
|
||||
{
|
||||
AZ_TracePrintf("AssetBuilder", "Builder ID: %s\n", response.m_uuid.ToString<AZStd::string>().c_str());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AssetBuilderComponent::Run()
|
||||
{
|
||||
AZ_TracePrintf("AssetBuilderComponent", "Run: Parsing command line.\n");
|
||||
@@ -218,8 +253,8 @@ bool AssetBuilderComponent::Run()
|
||||
}
|
||||
|
||||
AZStd::string task;
|
||||
|
||||
AZStd::string debugFile;
|
||||
|
||||
if (GetParameter(s_paramDebug, debugFile, false))
|
||||
{
|
||||
task = s_taskDebug;
|
||||
@@ -257,11 +292,13 @@ bool AssetBuilderComponent::Run()
|
||||
AZ_TracePrintf("AssetBuilderComponent", "Run: Connecting back to Asset Processor...\n");
|
||||
bool connectedToAssetProcessor = ConnectToAssetProcessor();
|
||||
//AP connection is required to access the asset catalog
|
||||
AZ_Error("AssetBuilder", connectedToAssetProcessor, "Failed to establish a network connection to the AssetProcessor. Use -help for options.");;
|
||||
AZ_Error("AssetBuilder", connectedToAssetProcessor, "Failed to establish a network connection to the AssetProcessor. Use -help for options.");
|
||||
|
||||
bool registerBuilders = commandLine->GetNumSwitchValues(s_paramRegisterBuilders) > 0;
|
||||
|
||||
IBuilderApplication* builderApplication = AZ::Interface<IBuilderApplication>::Get();
|
||||
|
||||
if(!builderApplication)
|
||||
if (!builderApplication)
|
||||
{
|
||||
AZ_Error("AssetBuilder", false, "Failed to retreive IBuilderApplication interface");
|
||||
return false;
|
||||
@@ -275,7 +312,7 @@ bool AssetBuilderComponent::Run()
|
||||
{
|
||||
if (task == s_taskResident)
|
||||
{
|
||||
result = RunInResidentMode();
|
||||
result = RunInResidentMode(registerBuilders);
|
||||
}
|
||||
else if (task == s_taskDebug)
|
||||
{
|
||||
@@ -371,43 +408,46 @@ bool AssetBuilderComponent::ConnectToAssetProcessor()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool AssetBuilderComponent::RunInResidentMode()
|
||||
bool AssetBuilderComponent::SendRegisteredBuildersToAp()
|
||||
{
|
||||
using namespace AssetBuilderSDK;
|
||||
AssetBuilder::BuilderRegistrationRequest registrationRequest;
|
||||
|
||||
for (const auto& [uuid, desc] : m_assetBuilderDescMap)
|
||||
{
|
||||
AssetBuilder::BuilderRegistration registration;
|
||||
|
||||
registration.m_name = desc->m_name;
|
||||
registration.m_analysisFingerprint = desc->m_analysisFingerprint;
|
||||
registration.m_flags = desc->m_flags;
|
||||
registration.m_flagsByJobKey = desc->m_flagsByJobKey;
|
||||
registration.m_version = desc->m_version;
|
||||
registration.m_busId = desc->m_busId;
|
||||
registration.m_patterns = desc->m_patterns;
|
||||
registration.m_productsToKeepOnFailure = desc->m_productsToKeepOnFailure;
|
||||
|
||||
registrationRequest.m_builders.push_back(AZStd::move(registration));
|
||||
}
|
||||
|
||||
bool result = SendRequest(registrationRequest);
|
||||
|
||||
AZ_Error("AssetBuilder", result, "Failed to send builder registration request to Asset Processor");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AssetBuilderComponent::RunInResidentMode(bool sendRegistration)
|
||||
{
|
||||
using namespace AssetBuilder;
|
||||
using namespace AZStd::placeholders;
|
||||
|
||||
AZ_TracePrintf("AssetBuilderComponent", "RunInResidentMode: Starting resident mode (waiting for commands to arrive)\n");
|
||||
|
||||
AZStd::string port, id, builderFolder;
|
||||
|
||||
if (!GetParameter(s_paramId, id)
|
||||
|| !GetParameter(s_paramModule, builderFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadBuilders(builderFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AzFramework::SocketConnection::GetInstance()->AddMessageHandler(CreateJobsNetRequest::MessageType(), AZStd::bind(&AssetBuilderComponent::CreateJobsResidentHandler, this, _1, _2, _3, _4));
|
||||
AzFramework::SocketConnection::GetInstance()->AddMessageHandler(ProcessJobNetRequest::MessageType(), AZStd::bind(&AssetBuilderComponent::ProcessJobResidentHandler, this, _1, _2, _3, _4));
|
||||
|
||||
BuilderHelloRequest request;
|
||||
BuilderHelloResponse response;
|
||||
bool result = DoHelloPing() && ((sendRegistration && SendRegisteredBuildersToAp()) || !sendRegistration);
|
||||
|
||||
request.m_uuid = AZ::Uuid::CreateString(id.c_str());
|
||||
|
||||
AZ_TracePrintf("AssetBuilderComponent", "RunInResidentMode: Pinging asset processor with the builder UUID %s\n", request.m_uuid.ToString<AZStd::string>().c_str());
|
||||
|
||||
bool result = AzFramework::AssetSystem::SendRequest(request, response);
|
||||
|
||||
AZ_Error("AssetBuilder", result, "Failed to send hello request to Asset Processor");
|
||||
// This error is only shown if we successfully got a response AND the response explicitly indicates the AP rejected the builder
|
||||
AZ_Error("AssetBuilder", !result || response.m_accepted, "Asset Processor rejected connection request");
|
||||
|
||||
if (result && response.m_accepted)
|
||||
if (result)
|
||||
{
|
||||
m_running = true;
|
||||
|
||||
@@ -416,7 +456,6 @@ bool AssetBuilderComponent::RunInResidentMode()
|
||||
|
||||
AzFramework::EngineConnectionEvents::Bus::Handler::BusConnect(); // Listen for disconnects
|
||||
|
||||
AZ_TracePrintf("AssetBuilder", "Builder ID: %s\n", response.m_uuid.ToString<AZStd::string>().c_str());
|
||||
AZ_TracePrintf("AssetBuilder", "Resident mode ready\n");
|
||||
m_mainEvent.acquire();
|
||||
AZ_TracePrintf("AssetBuilder", "Shutting down\n");
|
||||
@@ -737,11 +776,7 @@ bool AssetBuilderComponent::RunOneShotTask(const AZStd::string& task)
|
||||
|
||||
AZ::StringFunc::Path::Normalize(inputFilePath);
|
||||
AZ::StringFunc::Path::Normalize(outputFilePath);
|
||||
if (task == s_taskRegisterBuilder)
|
||||
{
|
||||
return HandleRegisterBuilder(inputFilePath, outputFilePath);
|
||||
}
|
||||
else if (task == s_taskCreateJob)
|
||||
if (task == s_taskCreateJob)
|
||||
{
|
||||
auto func = [this](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
|
||||
{
|
||||
@@ -897,7 +932,7 @@ void AssetBuilderComponent::JobThread()
|
||||
{
|
||||
case JobType::Create:
|
||||
{
|
||||
using namespace AssetBuilderSDK;
|
||||
using namespace AssetBuilder;
|
||||
|
||||
auto* netRequest = azrtti_cast<CreateJobsNetRequest*>(job->m_netRequest.get());
|
||||
auto* netResponse = azrtti_cast<CreateJobsNetResponse*>(job->m_netResponse.get());
|
||||
@@ -923,7 +958,7 @@ void AssetBuilderComponent::JobThread()
|
||||
}
|
||||
case JobType::Process:
|
||||
{
|
||||
using namespace AssetBuilderSDK;
|
||||
using namespace AssetBuilder;
|
||||
|
||||
AZ_TracePrintf("AssetBuilder", "Running processJob task\n");
|
||||
|
||||
@@ -982,14 +1017,14 @@ void AssetBuilderComponent::JobThread()
|
||||
|
||||
void AssetBuilderComponent::CreateJobsResidentHandler(AZ::u32 /*typeId*/, AZ::u32 serial, const void* data, AZ::u32 dataLength)
|
||||
{
|
||||
using namespace AssetBuilderSDK;
|
||||
using namespace AssetBuilder;
|
||||
|
||||
ResidentJobHandler<CreateJobsNetRequest, CreateJobsNetResponse>(serial, data, dataLength, JobType::Create);
|
||||
}
|
||||
|
||||
void AssetBuilderComponent::ProcessJobResidentHandler(AZ::u32 /*typeId*/, AZ::u32 serial, const void* data, AZ::u32 dataLength)
|
||||
{
|
||||
using namespace AssetBuilderSDK;
|
||||
using namespace AssetBuilder;
|
||||
|
||||
ResidentJobHandler<ProcessJobNetRequest, ProcessJobNetResponse>(serial, data, dataLength, JobType::Process);
|
||||
}
|
||||
@@ -1019,18 +1054,6 @@ bool AssetBuilderComponent::HandleTask(const AZStd::string& inputFilePath, const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetBuilderComponent::HandleRegisterBuilder(const AZStd::string& /*inputFilePath*/, const AZStd::string& outputFilePath) const
|
||||
{
|
||||
AssetBuilderSDK::RegisterBuilderResponse response;
|
||||
|
||||
for (const auto& pair : m_assetBuilderDescMap)
|
||||
{
|
||||
response.m_assetBuilderDescList.push_back(*pair.second);
|
||||
}
|
||||
|
||||
return AZ::Utils::SaveObjectToFile(outputFilePath, AZ::DataStream::ST_XML, &response);
|
||||
}
|
||||
|
||||
void AssetBuilderComponent::UpdateResultCode(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
if (request.m_jobDescription.m_failOnError)
|
||||
|
||||
@@ -46,6 +46,7 @@ class AssetBuilderComponent
|
||||
public:
|
||||
AZ_COMPONENT(AssetBuilderComponent, "{04332899-5d73-4d41-86b7-b1017d349673}")
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
bool DoHelloPing();
|
||||
|
||||
AssetBuilderComponent() = default;
|
||||
~AssetBuilderComponent() override = default;
|
||||
@@ -64,7 +65,7 @@ public:
|
||||
|
||||
void RegisterBuilderInformation(const AssetBuilderSDK::AssetBuilderDesc& builderDesc) override;
|
||||
void RegisterComponentDescriptor(AZ::ComponentDescriptor* descriptor) override;
|
||||
|
||||
|
||||
//EngineConnectionEvents Handler
|
||||
void Disconnected(AzFramework::SocketConnection* connection) override;
|
||||
|
||||
@@ -98,12 +99,13 @@ protected:
|
||||
static const char* GetLibraryExtension();
|
||||
|
||||
bool ConnectToAssetProcessor();
|
||||
bool SendRegisteredBuildersToAp();
|
||||
bool LoadBuilders(const AZStd::string& builderFolder);
|
||||
bool LoadBuilder(const AZStd::string& filePath);
|
||||
void UnloadBuilders();
|
||||
|
||||
//! Hooks up net job request handling and keeps the AssetBuilder running indefinitely
|
||||
bool RunInResidentMode();
|
||||
bool RunInResidentMode(bool sendRegistration);
|
||||
bool RunDebugTask(AZStd::string&& debugFile, bool runCreateJobs, bool runProcessJob);
|
||||
bool RunOneShotTask(const AZStd::string& task);
|
||||
|
||||
@@ -120,9 +122,6 @@ protected:
|
||||
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobFunction& job, const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& outResponse);
|
||||
|
||||
//! Handles a builder registration request
|
||||
bool HandleRegisterBuilder(const AZStd::string& inputFilePath, const AZStd::string& outputFilePath) const;
|
||||
|
||||
//! If needed looks at collected data and updates the result code from the job accordingly.
|
||||
void UpdateResultCode(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
@@ -141,7 +140,7 @@ protected:
|
||||
|
||||
//! Currently loading builder
|
||||
AssetBuilder::ExternalModuleAssetBuilderInfo* m_currentAssetBuilder = nullptr;
|
||||
|
||||
|
||||
//! Thread for running a job, so we don't block the network thread while doing work
|
||||
AZStd::thread_desc m_jobThreadDesc;
|
||||
AZStd::thread m_jobThread;
|
||||
@@ -153,7 +152,7 @@ protected:
|
||||
AZStd::binary_semaphore m_mainEvent;
|
||||
//! Use to signal a new job is ready to be processed
|
||||
AZStd::binary_semaphore m_jobEvent;
|
||||
|
||||
|
||||
//! Lock for m_queuedJob
|
||||
AZStd::mutex m_jobMutex;
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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 <AssetBuilderStatic.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
|
||||
namespace AssetBuilder
|
||||
{
|
||||
void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
BuilderRegistrationRequest::Reflect(context);
|
||||
|
||||
BuilderHelloRequest::Reflect(context);
|
||||
BuilderHelloResponse::Reflect(context);
|
||||
CreateJobsNetRequest::Reflect(context);
|
||||
CreateJobsNetResponse::Reflect(context);
|
||||
ProcessJobNetRequest::Reflect(context);
|
||||
ProcessJobNetResponse::Reflect(context);
|
||||
}
|
||||
|
||||
void InitializeSerializationContext()
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
AZ_Assert(serializeContext, "Unable to retrieve serialize context.");
|
||||
|
||||
Reflect(serializeContext);
|
||||
}
|
||||
|
||||
void BuilderHelloRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderHelloRequest>()->Version(1)->Field("UUID", &BuilderHelloRequest::m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::BuilderHelloRequest", 0x213a7248);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void BuilderHelloResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderHelloResponse>()
|
||||
->Version(1)
|
||||
->Field("Accepted", &BuilderHelloResponse::m_accepted)
|
||||
->Field("UUID", &BuilderHelloResponse::m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloResponse::GetMessageType() const
|
||||
{
|
||||
return BuilderHelloRequest::MessageType();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateJobsNetRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<CreateJobsNetRequest>()->Version(1)->Field("Request", &CreateJobsNetRequest::m_request);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::CreateJobsNetRequest", 0xc48209c0);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void CreateJobsNetResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<CreateJobsNetResponse>()->Version(1)->Field("Response", &CreateJobsNetResponse::m_response);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetResponse::GetMessageType() const
|
||||
{
|
||||
return CreateJobsNetRequest::MessageType();
|
||||
}
|
||||
|
||||
void ProcessJobNetRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<ProcessJobNetRequest>()->Version(1)->Field("Request", &ProcessJobNetRequest::m_request);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::ProcessJobNetRequest", 0x479f340f);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void ProcessJobNetResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<ProcessJobNetResponse>()->Version(1)->Field("Response", &ProcessJobNetResponse::m_response);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetResponse::GetMessageType() const
|
||||
{
|
||||
return ProcessJobNetRequest::MessageType();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
void BuilderRegistration::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderRegistration>()
|
||||
->Version(1)
|
||||
->Field("Name", &BuilderRegistration::m_name)
|
||||
->Field("Patterns", &BuilderRegistration::m_patterns)
|
||||
->Field("BusId", &BuilderRegistration::m_busId)
|
||||
->Field("Version", &BuilderRegistration::m_version)
|
||||
->Field("AnalysisFingerprint", &BuilderRegistration::m_analysisFingerprint)
|
||||
->Field("Flags", &BuilderRegistration::m_flags)
|
||||
->Field("FlagsByJobKey", &BuilderRegistration::m_flagsByJobKey)
|
||||
->Field("ProductsToKeepOnFailure", &BuilderRegistration::m_productsToKeepOnFailure);
|
||||
}
|
||||
}
|
||||
|
||||
void BuilderRegistrationRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
BuilderRegistration::Reflect(context);
|
||||
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderRegistrationRequest, BaseAssetProcessorMessage>()->Version(1)->Field(
|
||||
"Builders", &BuilderRegistrationRequest::m_builders);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BuilderRegistrationRequest::GetMessageType() const
|
||||
{
|
||||
return BuilderRegistrationRequest::MessageType;
|
||||
}
|
||||
|
||||
} // namespace AssetBuilder
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Asset/AssetProcessorMessages.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
namespace AssetBuilder
|
||||
{
|
||||
void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
void InitializeSerializationContext();
|
||||
|
||||
//! BuilderHelloRequest is sent by an AssetBuilder that is attempting to connect to the AssetProcessor to register itself as a worker
|
||||
class BuilderHelloRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(BuilderHelloRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(BuilderHelloRequest, "{5fab5962-a1d8-42a5-bf7a-fb1a8c5a9588}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
//! Unique ID assigned to this builder to identify it
|
||||
AZ::Uuid m_uuid = AZ::Uuid::CreateNull();
|
||||
};
|
||||
|
||||
//! BuilderHelloResponse contains the AssetProcessor's response to a builder connection attempt, indicating if it is accepted and the ID
|
||||
//! that it was assigned
|
||||
class BuilderHelloResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(BuilderHelloResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(BuilderHelloResponse, "{5f3d7c11-6639-4c6f-980a-32be546903c2}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
//! Indicates if the builder was accepted by the AP
|
||||
bool m_accepted = false;
|
||||
|
||||
//! Unique ID assigned to the builder. If the builder isn't a local process, this is the ID assigned by the AP
|
||||
AZ::Uuid m_uuid = AZ::Uuid::CreateNull();
|
||||
};
|
||||
|
||||
class CreateJobsNetRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(CreateJobsNetRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(CreateJobsNetRequest, "{97fa717d-3a09-4d21-95c6-b2eafd773f1c}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
AssetBuilderSDK::CreateJobsRequest m_request;
|
||||
};
|
||||
|
||||
class CreateJobsNetResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(CreateJobsNetResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(CreateJobsNetResponse, "{b2c7c2d3-b60e-4b27-b699-43e0ba991c33}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
AssetBuilderSDK::CreateJobsResponse m_response;
|
||||
};
|
||||
|
||||
class ProcessJobNetRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ProcessJobNetRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(ProcessJobNetRequest, "{05288de1-020b-48db-b9de-715f17284efa}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
AssetBuilderSDK::ProcessJobRequest m_request;
|
||||
};
|
||||
|
||||
class ProcessJobNetResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ProcessJobNetResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(ProcessJobNetResponse, "{26ddf882-246c-4cfb-912f-9b8e389df4f6}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
AssetBuilderSDK::ProcessJobResponse m_response;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct BuilderRegistration
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(BuilderRegistration, AZ::OSAllocator, 0);
|
||||
AZ_TYPE_INFO(BuilderRegistration, "{36E785C3-5046-4568-870A-336C8249E453}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZStd::string m_name;
|
||||
AZStd::vector<AssetBuilderSDK::AssetBuilderPattern> m_patterns;
|
||||
AZ::Uuid m_busId;
|
||||
int m_version = 0;
|
||||
AZStd::string m_analysisFingerprint;
|
||||
AZ::u8 m_flags = 0;
|
||||
AZStd::unordered_map<AZStd::string, AZ::u8> m_flagsByJobKey;
|
||||
AZStd::unordered_map<AZStd::string, AZStd::unordered_set<AZ::u32>> m_productsToKeepOnFailure;
|
||||
};
|
||||
|
||||
class BuilderRegistrationRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(BuilderRegistrationRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(BuilderRegistrationRequest, "{FA9CF2D5-C847-47F3-979D-6C3AE061715C}", BaseAssetProcessorMessage);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static constexpr unsigned int MessageType = AZ_CRC_CE("AssetSystem::BuilderRegistrationRequest");
|
||||
|
||||
BuilderRegistrationRequest() = default;
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
AZStd::vector<BuilderRegistration> m_builders;
|
||||
};
|
||||
} // namespace AssetBuilder
|
||||
@@ -6,6 +6,22 @@
|
||||
#
|
||||
#
|
||||
|
||||
ly_add_target(
|
||||
NAME AssetBuilder.Static STATIC
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
asset_builder_static_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
AZ::AzToolsFramework
|
||||
AZ::AssetBuilderSDK
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME AssetBuilder EXECUTABLE
|
||||
NAMESPACE AZ
|
||||
@@ -17,6 +33,7 @@ ly_add_target(
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AssetBuilder.Static
|
||||
3rdParty::Qt::Core
|
||||
3rdParty::Qt::Gui
|
||||
3rdParty::Qt::Network
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AssetBuilderStatic.h
|
||||
AssetBuilderStatic.cpp
|
||||
)
|
||||
@@ -1172,19 +1172,10 @@ namespace AssetBuilderSDK
|
||||
JobProduct::Reflect(context);
|
||||
AssetBuilderDesc::Reflect(context);
|
||||
|
||||
RegisterBuilderRequest::Reflect(context);
|
||||
RegisterBuilderResponse::Reflect(context);
|
||||
CreateJobsRequest::Reflect(context);
|
||||
CreateJobsResponse::Reflect(context);
|
||||
ProcessJobRequest::Reflect(context);
|
||||
ProcessJobResponse::Reflect(context);
|
||||
|
||||
BuilderHelloRequest::Reflect(context);
|
||||
BuilderHelloResponse::Reflect(context);
|
||||
CreateJobsNetRequest::Reflect(context);
|
||||
CreateJobsNetResponse::Reflect(context);
|
||||
ProcessJobNetRequest::Reflect(context);
|
||||
ProcessJobNetResponse::Reflect(context);
|
||||
}
|
||||
|
||||
void InitializeSerializationContext()
|
||||
@@ -1263,24 +1254,6 @@ namespace AssetBuilderSDK
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterBuilderRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<RegisterBuilderRequest>()->
|
||||
Version(1)->
|
||||
Field("FilePath", &RegisterBuilderRequest::m_filePath);
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<RegisterBuilderRequest>("RegisterBuilderRequest")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset.builder")
|
||||
->Property("filePath", BehaviorValueProperty(&RegisterBuilderRequest::m_filePath));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBuilderDesc::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
@@ -1313,25 +1286,6 @@ namespace AssetBuilderSDK
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterBuilderResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<RegisterBuilderResponse>()
|
||||
->Version(1)
|
||||
->Field("Asset Builder Desc List", &RegisterBuilderResponse::m_assetBuilderDescList);
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<RegisterBuilderResponse>("RegisterBuilderResponse")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset.builder")
|
||||
->Constructor()
|
||||
->Property("assetBuilderDescList", BehaviorValueProperty(&RegisterBuilderResponse::m_assetBuilderDescList));
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateJobsResponse::Succeeded() const
|
||||
{
|
||||
return m_result == CreateJobsResultCode::Success;
|
||||
@@ -1362,128 +1316,6 @@ namespace AssetBuilderSDK
|
||||
}
|
||||
}
|
||||
|
||||
void BuilderHelloRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderHelloRequest>()
|
||||
->Version(1)
|
||||
->Field("UUID", &BuilderHelloRequest::m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::BuilderHelloRequest", 0x213a7248);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void BuilderHelloResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<BuilderHelloResponse>()
|
||||
->Version(1)
|
||||
->Field("Accepted", &BuilderHelloResponse::m_accepted)
|
||||
->Field("UUID", &BuilderHelloResponse::m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BuilderHelloResponse::GetMessageType() const
|
||||
{
|
||||
return BuilderHelloRequest::MessageType();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateJobsNetRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<CreateJobsNetRequest>()
|
||||
->Version(1)
|
||||
->Field("Request", &CreateJobsNetRequest::m_request);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::CreateJobsNetRequest", 0xc48209c0);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void CreateJobsNetResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<CreateJobsNetResponse>()
|
||||
->Version(1)
|
||||
->Field("Response", &CreateJobsNetResponse::m_response);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CreateJobsNetResponse::GetMessageType() const
|
||||
{
|
||||
return CreateJobsNetRequest::MessageType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ProcessJobNetRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<ProcessJobNetRequest>()
|
||||
->Version(1)
|
||||
->Field("Request", &ProcessJobNetRequest::m_request);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetRequest::MessageType()
|
||||
{
|
||||
static unsigned int messageType = AZ_CRC("AssetBuilderSDK::ProcessJobNetRequest", 0x479f340f);
|
||||
|
||||
return messageType;
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetRequest::GetMessageType() const
|
||||
{
|
||||
return MessageType();
|
||||
}
|
||||
|
||||
void ProcessJobNetResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<ProcessJobNetResponse>()
|
||||
->Version(1)
|
||||
->Field("Response", &ProcessJobNetResponse::m_response);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ProcessJobNetResponse::GetMessageType() const
|
||||
{
|
||||
return ProcessJobNetRequest::MessageType();
|
||||
}
|
||||
|
||||
JobDependency::JobDependency(const AZStd::string& jobKey, const AZStd::string& platformIdentifier, const JobDependencyType& type, const SourceFileDependency& sourceFile)
|
||||
: m_jobKey(jobKey)
|
||||
, m_platformIdentifier(platformIdentifier)
|
||||
|
||||
@@ -464,35 +464,6 @@ namespace AssetBuilderSDK
|
||||
AZStd::string m_platformIdentifier;
|
||||
};
|
||||
|
||||
//! RegisterBuilderRequest contains input data that will be sent by the AssetProcessor to the builder during the startup registration phase
|
||||
struct RegisterBuilderRequest
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(RegisterBuilderRequest, AZ::SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(RegisterBuilderRequest, "{7C6C5198-4766-42B8-9A1E-48479CE2F5EA}");
|
||||
|
||||
AZStd::string m_filePath;
|
||||
|
||||
RegisterBuilderRequest() {}
|
||||
|
||||
explicit RegisterBuilderRequest(const AZStd::string& filePath)
|
||||
: m_filePath(filePath)
|
||||
{
|
||||
}
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
//! INTERNAL USE ONLY - RegisterBuilderResponse contains registration data that will be sent by the builder to the AssetProcessor in response to RegisterBuilderRequest
|
||||
struct RegisterBuilderResponse
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(RegisterBuilderResponse, AZ::SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(RegisterBuilderResponse, "{0AE5583F-C763-410E-BA7F-78BD90546C01}");
|
||||
|
||||
AZStd::vector<AssetBuilderDesc> m_assetBuilderDescList;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
/**
|
||||
* This tells you about a platform in your CreateJobsRequest or your ProcessJobRequest
|
||||
*/
|
||||
@@ -756,99 +727,9 @@ namespace AssetBuilderSDK
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
//! BuilderHelloRequest is sent by an AssetBuilder that is attempting to connect to the AssetProcessor to register itself as a worker
|
||||
class BuilderHelloRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
AZ_CLASS_ALLOCATOR(BuilderHelloRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(BuilderHelloRequest, "{5fab5962-a1d8-42a5-bf7a-fb1a8c5a9588}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
//! Unique ID assigned to this builder to identify it
|
||||
AZ::Uuid m_uuid = AZ::Uuid::CreateNull();
|
||||
};
|
||||
|
||||
//! BuilderHelloResponse contains the AssetProcessor's response to a builder connection attempt, indicating if it is accepted and the ID that it was assigned
|
||||
class BuilderHelloResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(BuilderHelloResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(BuilderHelloResponse, "{5f3d7c11-6639-4c6f-980a-32be546903c2}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
//! Indicates if the builder was accepted by the AP
|
||||
bool m_accepted = false;
|
||||
|
||||
//! Unique ID assigned to the builder. If the builder isn't a local process, this is the ID assigned by the AP
|
||||
AZ::Uuid m_uuid = AZ::Uuid::CreateNull();
|
||||
};
|
||||
|
||||
class CreateJobsNetRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(CreateJobsNetRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(CreateJobsNetRequest, "{97fa717d-3a09-4d21-95c6-b2eafd773f1c}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
CreateJobsRequest m_request;
|
||||
};
|
||||
|
||||
class CreateJobsNetResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(CreateJobsNetResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(CreateJobsNetResponse, "{b2c7c2d3-b60e-4b27-b699-43e0ba991c33}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
CreateJobsResponse m_response;
|
||||
};
|
||||
|
||||
class ProcessJobNetRequest : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(ProcessJobNetRequest, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(ProcessJobNetRequest, "{05288de1-020b-48db-b9de-715f17284efa}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static unsigned int MessageType();
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
ProcessJobRequest m_request;
|
||||
};
|
||||
|
||||
class ProcessJobNetResponse : public AzFramework::AssetSystem::BaseAssetProcessorMessage
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(ProcessJobNetResponse, AZ::OSAllocator, 0);
|
||||
AZ_RTTI(ProcessJobNetResponse, "{26ddf882-246c-4cfb-912f-9b8e389df4f6}", BaseAssetProcessorMessage);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
unsigned int GetMessageType() const override;
|
||||
|
||||
ProcessJobResponse m_response;
|
||||
};
|
||||
|
||||
|
||||
//! JobCancelListener can be used by builders in their processJob method to listen for job cancellation request.
|
||||
//! The address of this listener is the jobid which can be found in the process job request.
|
||||
@@ -935,44 +816,3 @@ namespace AZ
|
||||
AZ_TYPE_INFO_SPECIALIZE(AssetBuilderSDK::ProductPathDependencyType, "{EF77742B-9627-4072-B431-396AA7183C80}");
|
||||
AZ_TYPE_INFO_SPECIALIZE(AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType, "{BE9C8805-DB17-4500-944A-EB33FD0BE347}");
|
||||
}
|
||||
|
||||
//! This macro should be used by every AssetBuilder to register itself,
|
||||
//! AssetProcessor uses these exported function to identify whether a dll is an Asset Builder or not
|
||||
//! If you want something highly custom you can do these entry points yourself instead of using the macro.
|
||||
#define REGISTER_ASSETBUILDER \
|
||||
extern void BuilderOnInit(); \
|
||||
extern void BuilderDestroy(); \
|
||||
extern void BuilderRegisterDescriptors(); \
|
||||
extern void BuilderAddComponents(AZ::Entity * entity); \
|
||||
extern "C" \
|
||||
{ \
|
||||
AZ_DLL_EXPORT int IsAssetBuilder() \
|
||||
{ \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
AZ_DLL_EXPORT void InitializeModule(AZ::EnvironmentInstance sharedEnvironment) \
|
||||
{ \
|
||||
AZ::Environment::Attach(sharedEnvironment); \
|
||||
BuilderOnInit(); \
|
||||
} \
|
||||
\
|
||||
AZ_DLL_EXPORT void UninitializeModule() \
|
||||
{ \
|
||||
BuilderDestroy(); \
|
||||
AZ::Environment::Detach(); \
|
||||
} \
|
||||
\
|
||||
AZ_DLL_EXPORT void ModuleRegisterDescriptors() \
|
||||
{ \
|
||||
BuilderRegisterDescriptors(); \
|
||||
} \
|
||||
\
|
||||
AZ_DLL_EXPORT void ModuleAddComponents(AZ::Entity * entity) \
|
||||
{ \
|
||||
BuilderAddComponents(entity); \
|
||||
} \
|
||||
}
|
||||
// confusion-reducing note: above end-brace is part of the macro, not a namespace
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ ly_add_target(
|
||||
AZ::AzQtComponents
|
||||
AZ::AzToolsFramework
|
||||
AZ::AssetBuilderSDK
|
||||
AZ::AssetBuilder.Static
|
||||
${additional_dependencies}
|
||||
RUNTIME_DEPENDENCIES
|
||||
AZ::AssetBuilder
|
||||
@@ -52,7 +53,7 @@ get_property(asset_builders GLOBAL PROPERTY LY_ASSET_BUILDERS)
|
||||
string (REPLACE ";" "," asset_builders "${asset_builders}")
|
||||
ly_add_source_properties(
|
||||
SOURCES native/utilities/ApplicationManager.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES LY_ASSET_BUILDERS="${asset_builders}"
|
||||
)
|
||||
|
||||
@@ -147,9 +148,9 @@ endif()
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
|
||||
ly_add_target(
|
||||
NAME AssetProcessor.Tests EXECUTABLE
|
||||
NAME AssetProcessor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE AZ
|
||||
AUTOMOC
|
||||
AUTORCC
|
||||
@@ -167,12 +168,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
)
|
||||
ly_add_source_properties(
|
||||
SOURCES native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES ${LY_PAL_TOOLS_DEFINES}
|
||||
)
|
||||
ly_add_source_properties(
|
||||
SOURCES native/unittests/AssetProcessorManagerUnitTests.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES LY_CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
|
||||
@@ -266,7 +267,10 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME AZ::AssetProcessor.Tests
|
||||
TEST_COMMAND $<TARGET_FILE:AZ::AssetProcessor.Tests> --unittest --gtest_filter=-*.SUITE_sandbox*
|
||||
)
|
||||
ly_add_googlebenchmark(
|
||||
NAME AZ::AssetProcessor.Benchmarks
|
||||
TARGET AZ::AssetProcessor.Tests
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -8,4 +8,6 @@
|
||||
|
||||
set(FILES
|
||||
native/FileWatcher/FileWatcher_linux.cpp
|
||||
native/FileWatcher/FileWatcher_linux.h
|
||||
native/FileWatcher/FileWatcher_platform.h
|
||||
)
|
||||
|
||||
+152
-163
@@ -5,7 +5,10 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
#include <native/FileWatcher/FileWatcher.h>
|
||||
#include <native/FileWatcher/FileWatcher_platform.h>
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QHash>
|
||||
@@ -15,155 +18,127 @@
|
||||
#include <sys/inotify.h>
|
||||
|
||||
|
||||
static constexpr int s_handleToFolderMapLockTimeout = 1000; // 1 sec timeout for obtaining the handle to folder map lock
|
||||
static constexpr size_t s_iNotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
|
||||
static constexpr size_t s_iNotifyEventSize = sizeof(struct inotify_event);
|
||||
static constexpr size_t s_iNotifyReadBufferSize = s_iNotifyMaxEntries * s_iNotifyEventSize;
|
||||
static constexpr size_t s_inotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
|
||||
static constexpr size_t s_inotifyEventSize = sizeof(struct inotify_event);
|
||||
static constexpr size_t s_inotifyReadBufferSize = s_inotifyMaxEntries * s_inotifyEventSize;
|
||||
|
||||
struct FolderRootWatch::PlatformImplementation
|
||||
bool FileWatcher::PlatformImplementation::Initialize()
|
||||
{
|
||||
PlatformImplementation() = default;
|
||||
|
||||
int m_iNotifyHandle = -1;
|
||||
QMutex m_handleToFolderMapLock;
|
||||
QHash<int, QString> m_handleToFolderMap;
|
||||
|
||||
bool Initialize()
|
||||
if (m_inotifyHandle < 0)
|
||||
{
|
||||
if (m_iNotifyHandle < 0)
|
||||
{
|
||||
// The CLOEXEC flag prevents the inotify watchers from copying on fork/exec
|
||||
m_iNotifyHandle = inotify_init1(IN_CLOEXEC);
|
||||
}
|
||||
return (m_iNotifyHandle >= 0);
|
||||
// The CLOEXEC flag prevents the inotify watchers from copying on fork/exec
|
||||
m_inotifyHandle = inotify_init1(IN_CLOEXEC);
|
||||
|
||||
[[maybe_unused]] const auto err = errno;
|
||||
[[maybe_unused]] AZStd::fixed_string<255> errorString;
|
||||
AZ_Warning("FileWatcher", (m_inotifyHandle >= 0), "Unable to initialize inotify, file monitoring will not be available: %s\n", strerror_r(err, errorString.data(), errorString.capacity()));
|
||||
}
|
||||
|
||||
void Finalize()
|
||||
{
|
||||
if (m_iNotifyHandle >= 0)
|
||||
{
|
||||
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
|
||||
{
|
||||
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
|
||||
return;
|
||||
}
|
||||
|
||||
QHashIterator<int, QString> iter(m_handleToFolderMap);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
iter.next();
|
||||
int watchHandle = iter.key();
|
||||
inotify_rm_watch(m_iNotifyHandle, watchHandle);
|
||||
}
|
||||
m_handleToFolderMap.clear();
|
||||
m_handleToFolderMapLock.unlock();
|
||||
|
||||
::close(m_iNotifyHandle);
|
||||
m_iNotifyHandle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void AddWatchFolder(QString folder)
|
||||
{
|
||||
if (m_iNotifyHandle >= 0)
|
||||
{
|
||||
// Clean up the path before accepting it as a watch folder
|
||||
QString cleanPath = QDir::cleanPath(folder);
|
||||
|
||||
// Add the folder to watch and track it
|
||||
int watchHandle = inotify_add_watch(m_iNotifyHandle,
|
||||
cleanPath.toUtf8().constData(),
|
||||
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
|
||||
|
||||
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
|
||||
{
|
||||
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
|
||||
return;
|
||||
}
|
||||
m_handleToFolderMap[watchHandle] = cleanPath;
|
||||
m_handleToFolderMapLock.unlock();
|
||||
|
||||
// Add all the subfolders to watch and track them
|
||||
QDirIterator dirIter(folder, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
QString dirName = dirIter.next();
|
||||
if (dirName.endsWith("/.") || dirName.endsWith("/.."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int watchHandle = inotify_add_watch(m_iNotifyHandle,
|
||||
dirName.toUtf8().constData(),
|
||||
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
|
||||
|
||||
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
|
||||
{
|
||||
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
|
||||
return;
|
||||
}
|
||||
m_handleToFolderMap[watchHandle] = dirName;
|
||||
m_handleToFolderMapLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveWatchFolder(int watchHandle)
|
||||
{
|
||||
if (m_iNotifyHandle >= 0)
|
||||
{
|
||||
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
|
||||
{
|
||||
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
|
||||
return;
|
||||
}
|
||||
|
||||
QHash<int, QString>::iterator handleToRemove = m_handleToFolderMap.find(watchHandle);
|
||||
if (handleToRemove != m_handleToFolderMap.end())
|
||||
{
|
||||
inotify_rm_watch(m_iNotifyHandle, watchHandle);
|
||||
m_handleToFolderMap.erase(handleToRemove);
|
||||
}
|
||||
|
||||
m_handleToFolderMapLock.unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// FolderWatchRoot
|
||||
FolderRootWatch::FolderRootWatch(const QString rootFolder)
|
||||
: m_root(rootFolder)
|
||||
, m_shutdownThreadSignal(false)
|
||||
, m_fileWatcher(nullptr)
|
||||
, m_platformImpl(new PlatformImplementation())
|
||||
{
|
||||
return (m_inotifyHandle >= 0);
|
||||
}
|
||||
|
||||
FolderRootWatch::~FolderRootWatch()
|
||||
void FileWatcher::PlatformImplementation::Finalize()
|
||||
{
|
||||
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
|
||||
Stop();
|
||||
if (m_inotifyHandle < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
delete m_platformImpl;
|
||||
{
|
||||
QMutexLocker lock{&m_handleToFolderMapLock};
|
||||
for (const auto& watchHandle : m_handleToFolderMap.keys())
|
||||
{
|
||||
inotify_rm_watch(m_inotifyHandle, watchHandle);
|
||||
}
|
||||
m_handleToFolderMap.clear();
|
||||
}
|
||||
|
||||
::close(m_inotifyHandle);
|
||||
m_inotifyHandle = -1;
|
||||
}
|
||||
|
||||
bool FolderRootWatch::Start()
|
||||
void FileWatcher::PlatformImplementation::AddWatchFolder(QString folder, bool recursive)
|
||||
{
|
||||
if (m_inotifyHandle < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up the path before accepting it as a watch folder
|
||||
QString cleanPath = QDir::cleanPath(folder);
|
||||
|
||||
// Add the folder to watch and track it
|
||||
int watchHandle = inotify_add_watch(m_inotifyHandle,
|
||||
cleanPath.toUtf8().constData(),
|
||||
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
|
||||
|
||||
if (watchHandle < 0)
|
||||
{
|
||||
[[maybe_unused]] const auto err = errno;
|
||||
[[maybe_unused]] AZStd::fixed_string<255> errorString;
|
||||
AZ_Warning("FileWatcher", false, "inotify_add_watch failed for path %s: %s", cleanPath.toUtf8().constData(), strerror_r(err, errorString.data(), errorString.capacity()));
|
||||
return;
|
||||
}
|
||||
{
|
||||
QMutexLocker lock{&m_handleToFolderMapLock};
|
||||
m_handleToFolderMap[watchHandle] = cleanPath;
|
||||
}
|
||||
|
||||
// Add all the contents (files and directories) to watch and track them
|
||||
QDirIterator dirIter(folder, QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files, (recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags) | QDirIterator::FollowSymlinks);
|
||||
|
||||
while (dirIter.hasNext())
|
||||
{
|
||||
QString dirName = dirIter.next();
|
||||
|
||||
watchHandle = inotify_add_watch(m_inotifyHandle,
|
||||
dirName.toUtf8().constData(),
|
||||
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE);
|
||||
if (watchHandle < 0)
|
||||
{
|
||||
[[maybe_unused]] const auto err = errno;
|
||||
[[maybe_unused]] AZStd::fixed_string<255> errorString;
|
||||
AZ_Warning("FileWatcher", false, "inotify_add_watch failed for path %s: %s", dirName.toUtf8().constData(), strerror_r(err, errorString.data(), errorString.capacity()));
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker lock{&m_handleToFolderMapLock};
|
||||
m_handleToFolderMap[watchHandle] = dirName;
|
||||
}
|
||||
}
|
||||
|
||||
void FileWatcher::PlatformImplementation::RemoveWatchFolder(int watchHandle)
|
||||
{
|
||||
if (m_inotifyHandle < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker lock{&m_handleToFolderMapLock};
|
||||
if (m_handleToFolderMap.remove(watchHandle))
|
||||
{
|
||||
inotify_rm_watch(m_inotifyHandle, watchHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileWatcher::PlatformStart()
|
||||
{
|
||||
// inotify will be used by linux to monitor file changes within directories under the root folder
|
||||
if (!m_platformImpl->Initialize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_platformImpl->AddWatchFolder(m_root);
|
||||
for (const auto& [directory, recursive] : m_folderWatchRoots)
|
||||
{
|
||||
if (QDir(directory).exists())
|
||||
{
|
||||
m_platformImpl->AddWatchFolder(directory, recursive);
|
||||
}
|
||||
}
|
||||
|
||||
m_shutdownThreadSignal = false;
|
||||
m_thread = std::thread([this]() { WatchFolderLoop(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void FolderRootWatch::Stop()
|
||||
void FileWatcher::PlatformStop()
|
||||
{
|
||||
m_shutdownThreadSignal = true;
|
||||
|
||||
@@ -172,64 +147,78 @@ void FolderRootWatch::Stop()
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join(); // wait for the thread to finish
|
||||
m_thread = std::thread(); //destroy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FolderRootWatch::WatchFolderLoop()
|
||||
void FileWatcher::WatchFolderLoop()
|
||||
{
|
||||
char eventBuffer[s_iNotifyReadBufferSize];
|
||||
char eventBuffer[s_inotifyReadBufferSize];
|
||||
while (!m_shutdownThreadSignal)
|
||||
{
|
||||
ssize_t bytesRead = ::read(m_platformImpl->m_iNotifyHandle, eventBuffer, s_iNotifyReadBufferSize);
|
||||
ssize_t bytesRead = ::read(m_platformImpl->m_inotifyHandle, eventBuffer, s_inotifyReadBufferSize);
|
||||
if (bytesRead < 0)
|
||||
{
|
||||
// Break out of the loop when the notify handle was closed (outside of this thread)
|
||||
break;
|
||||
}
|
||||
else if (bytesRead > 0)
|
||||
if (!bytesRead)
|
||||
{
|
||||
for (size_t index=0; index<bytesRead;)
|
||||
continue;
|
||||
}
|
||||
for (size_t index=0; index<bytesRead;)
|
||||
{
|
||||
const auto* event = reinterpret_cast<inotify_event*>(&eventBuffer[index]);
|
||||
|
||||
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE))
|
||||
{
|
||||
struct inotify_event *event = ( struct inotify_event * ) &eventBuffer[ index ];
|
||||
const QString pathStr = QDir(m_platformImpl->m_handleToFolderMap[event->wd]).absoluteFilePath(event->name);
|
||||
|
||||
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE ))
|
||||
if (event->mask & (IN_CREATE | IN_MOVED_TO))
|
||||
{
|
||||
QString pathStr = QString("%1%2%3").arg(m_platformImpl->m_handleToFolderMap[event->wd], QDir::separator(), event->name);
|
||||
if (event->mask & IN_ISDIR)
|
||||
{
|
||||
// New Directory, see if it should be added to the watched directories
|
||||
// It is only added if it is a child of a recursively watched directory
|
||||
const auto found = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [this, event](const WatchRoot& watchRoot)
|
||||
{
|
||||
return watchRoot.m_directory == m_platformImpl->m_handleToFolderMap[event->wd];
|
||||
});
|
||||
|
||||
if (event->mask & (IN_CREATE | IN_MOVED_TO))
|
||||
{
|
||||
if ( event->mask & IN_ISDIR )
|
||||
// If the path is not in m_folderWatchRoots, it must
|
||||
// be a new subdirectory of a subdirectory of some
|
||||
// other root that is being watched recursively.
|
||||
// Maintain the recursive nature of that root.
|
||||
const bool shouldAddFolder = (found == end(m_folderWatchRoots)) ? true : found->m_recursive;
|
||||
|
||||
if (shouldAddFolder)
|
||||
{
|
||||
// New Directory, add it to the watch
|
||||
m_platformImpl->AddWatchFolder(pathStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessNewFileEvent(pathStr);
|
||||
m_platformImpl->AddWatchFolder(pathStr, true);
|
||||
}
|
||||
}
|
||||
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
|
||||
else
|
||||
{
|
||||
if (event->mask & IN_ISDIR)
|
||||
{
|
||||
// Directory Deleted, remove it from the watch
|
||||
m_platformImpl->RemoveWatchFolder(event->wd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessDeleteFileEvent(pathStr);
|
||||
}
|
||||
}
|
||||
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
|
||||
{
|
||||
ProcessModifyFileEvent(pathStr);
|
||||
rawFileAdded(pathStr, {});
|
||||
}
|
||||
}
|
||||
index += s_iNotifyEventSize + event->len;
|
||||
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
|
||||
{
|
||||
if (event->mask & IN_ISDIR)
|
||||
{
|
||||
// Directory Deleted, remove it from the watch
|
||||
m_platformImpl->RemoveWatchFolder(event->wd);
|
||||
}
|
||||
else
|
||||
{
|
||||
rawFileRemoved(pathStr, {});
|
||||
}
|
||||
}
|
||||
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
|
||||
{
|
||||
rawFileModified(pathStr, {});
|
||||
}
|
||||
}
|
||||
index += s_inotifyEventSize + event->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FileWatcher/FileWatcher.h>
|
||||
#include <QMutex>
|
||||
#include <QHash>
|
||||
|
||||
class FileWatcher::PlatformImplementation
|
||||
{
|
||||
public:
|
||||
bool Initialize();
|
||||
void Finalize();
|
||||
void AddWatchFolder(QString folder, bool recursive);
|
||||
void RemoveWatchFolder(int watchHandle);
|
||||
|
||||
int m_inotifyHandle = -1;
|
||||
QMutex m_handleToFolderMapLock;
|
||||
QHash<int, QString> m_handleToFolderMap;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <native/FileWatcher/FileWatcher_linux.h>
|
||||
@@ -8,4 +8,6 @@
|
||||
|
||||
set(FILES
|
||||
native/FileWatcher/FileWatcher_macos.cpp
|
||||
native/FileWatcher/FileWatcher_mac.h
|
||||
native/FileWatcher/FileWatcher_platform.h
|
||||
)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <native/FileWatcher/FileWatcher.h>
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
class FileWatcher::PlatformImplementation
|
||||
{
|
||||
public:
|
||||
FSEventStreamRef m_stream = nullptr;
|
||||
CFRunLoopRef m_runLoop = nullptr;
|
||||
};
|
||||
@@ -6,47 +6,23 @@
|
||||
*
|
||||
*/
|
||||
#include <native/FileWatcher/FileWatcher.h>
|
||||
#include <native/FileWatcher/FileWatcher_platform.h>
|
||||
|
||||
#include <native/utilities/BatchApplicationManager.h>
|
||||
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]);
|
||||
|
||||
struct FolderRootWatch::PlatformImplementation
|
||||
{
|
||||
PlatformImplementation() : m_stream(nullptr), m_runLoop(nullptr) { }
|
||||
|
||||
FSEventStreamRef m_stream;
|
||||
CFRunLoopRef m_runLoop;
|
||||
QString m_renameFileDirectory;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// FolderWatchRoot
|
||||
FolderRootWatch::FolderRootWatch(const QString rootFolder)
|
||||
: m_root(rootFolder)
|
||||
, m_shutdownThreadSignal(false)
|
||||
, m_fileWatcher(nullptr)
|
||||
, m_platformImpl(new PlatformImplementation())
|
||||
{
|
||||
}
|
||||
|
||||
FolderRootWatch::~FolderRootWatch()
|
||||
{
|
||||
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
|
||||
Stop();
|
||||
|
||||
delete m_platformImpl;
|
||||
}
|
||||
|
||||
bool FolderRootWatch::Start()
|
||||
bool FileWatcher::PlatformStart()
|
||||
{
|
||||
m_shutdownThreadSignal = false;
|
||||
|
||||
CFStringRef rootPath = CFStringCreateWithCString(kCFAllocatorDefault, m_root.toStdString().data(), kCFStringEncodingMacRoman);
|
||||
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&rootPath, 1, NULL);
|
||||
CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, this->m_folderWatchRoots.size(), nullptr);
|
||||
for (const auto& root : this->m_folderWatchRoots)
|
||||
{
|
||||
CFArrayAppendValue(pathsToWatch, root.m_directory.toCFString());
|
||||
}
|
||||
|
||||
// The larger this number, the larger the delay between the kernel knowing a file changed
|
||||
// and us actually consuming the event. It is very important for asset processor to deal with
|
||||
@@ -60,11 +36,12 @@ bool FolderRootWatch::Start()
|
||||
// Set ourselves as the value for the context info field so that in the callback
|
||||
// we get passed into it and the callback can call our public API to handle
|
||||
// the file change events
|
||||
FSEventStreamContext streamContext;
|
||||
::memset(&streamContext, 0, sizeof(streamContext));
|
||||
streamContext.info = this;
|
||||
FSEventStreamContext streamContext{
|
||||
/*.version =*/ 0,
|
||||
/*.info =*/ this,
|
||||
};
|
||||
|
||||
m_platformImpl->m_stream = FSEventStreamCreate(NULL,
|
||||
m_platformImpl->m_stream = FSEventStreamCreate(nullptr,
|
||||
FileEventStreamCallback,
|
||||
&streamContext,
|
||||
pathsToWatch,
|
||||
@@ -72,24 +49,25 @@ bool FolderRootWatch::Start()
|
||||
timeBetweenKernelUpdateAndNotification,
|
||||
kFSEventStreamCreateFlagFileEvents);
|
||||
|
||||
AZ_Error("FileWatcher", (m_platformImpl->m_stream != nullptr), "FSEventStreamCreate returned a nullptr. No file events will be reported for %s", m_root.toStdString().c_str());
|
||||
|
||||
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
|
||||
AZ_Error("FileWatcher", (m_platformImpl->m_stream != nullptr), "FSEventStreamCreate returned a nullptr. No file events will be reported.");
|
||||
|
||||
const CFIndex pathCount = CFArrayGetCount(pathsToWatch);
|
||||
for(CFIndex i = 0; i < pathCount; ++i)
|
||||
{
|
||||
CFRelease(CFArrayGetValueAtIndex(pathsToWatch, i));
|
||||
}
|
||||
CFRelease(pathsToWatch);
|
||||
CFRelease(rootPath);
|
||||
|
||||
return (m_platformImpl->m_stream != nullptr);
|
||||
return m_platformImpl->m_stream != nullptr;
|
||||
}
|
||||
|
||||
void FolderRootWatch::Stop()
|
||||
void FileWatcher::PlatformStop()
|
||||
{
|
||||
m_shutdownThreadSignal = true;
|
||||
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join(); // wait for the thread to finish
|
||||
m_thread = std::thread(); //destroy
|
||||
}
|
||||
|
||||
FSEventStreamStop(m_platformImpl->m_stream);
|
||||
@@ -97,7 +75,7 @@ void FolderRootWatch::Stop()
|
||||
FSEventStreamRelease(m_platformImpl->m_stream);
|
||||
}
|
||||
|
||||
void FolderRootWatch::WatchFolderLoop()
|
||||
void FileWatcher::WatchFolderLoop()
|
||||
{
|
||||
// Use a half second timeout interval so that we can check if
|
||||
// m_shutdownThreadSignal has been changed while we were running the RunLoop
|
||||
@@ -117,14 +95,14 @@ void FolderRootWatch::WatchFolderLoop()
|
||||
|
||||
void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
|
||||
{
|
||||
FolderRootWatch* watcher = reinterpret_cast<FolderRootWatch*>(clientCallBackInfo);
|
||||
auto* watcher = reinterpret_cast<FileWatcher*>(clientCallBackInfo);
|
||||
|
||||
const char** filePaths = reinterpret_cast<const char**>(eventPaths);
|
||||
|
||||
for (int i = 0; i < numEvents; ++i)
|
||||
{
|
||||
QFileInfo fileInfo(QDir::cleanPath(filePaths[i]));
|
||||
QString fileAndPath = fileInfo.absoluteFilePath();
|
||||
const QFileInfo fileInfo(QDir::cleanPath(filePaths[i]));
|
||||
const QString fileAndPath = fileInfo.absoluteFilePath();
|
||||
|
||||
if (!fileInfo.isHidden())
|
||||
{
|
||||
@@ -133,38 +111,38 @@ void FileEventStreamCallback(ConstFSEventStreamRef streamRef, void *clientCallBa
|
||||
// so check for all of them
|
||||
if (eventFlags[i] & kFSEventStreamEventFlagItemCreated)
|
||||
{
|
||||
watcher->ProcessNewFileEvent(fileAndPath);
|
||||
watcher->rawFileAdded(fileAndPath, {});
|
||||
}
|
||||
|
||||
if (eventFlags[i] & kFSEventStreamEventFlagItemModified)
|
||||
{
|
||||
watcher->ProcessModifyFileEvent(fileAndPath);
|
||||
watcher->rawFileModified(fileAndPath, {});
|
||||
}
|
||||
|
||||
if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved)
|
||||
{
|
||||
watcher->ProcessDeleteFileEvent(fileAndPath);
|
||||
watcher->rawFileRemoved(fileAndPath, {});
|
||||
}
|
||||
|
||||
if (eventFlags[i] & kFSEventStreamEventFlagItemRenamed)
|
||||
{
|
||||
if (fileInfo.exists())
|
||||
{
|
||||
watcher->ProcessNewFileEvent(fileAndPath);
|
||||
watcher->rawFileAdded(fileAndPath, {});
|
||||
|
||||
// macOS does not send out an event for the directory being
|
||||
// modified when a file has been renamed but the FileWatcher
|
||||
// API expects it so send out the modification event ourselves.
|
||||
watcher->ProcessModifyFileEvent(fileInfo.absolutePath());
|
||||
watcher->rawFileModified(fileInfo.absolutePath(), {});
|
||||
}
|
||||
else
|
||||
{
|
||||
watcher->ProcessDeleteFileEvent(fileAndPath);
|
||||
watcher->rawFileRemoved(fileAndPath, {});
|
||||
|
||||
// macOS does not send out an event for the directory being
|
||||
// modified when a file has been renamed but the FileWatcher
|
||||
// API expects it so send out the modification event ourselves.
|
||||
watcher->ProcessModifyFileEvent(fileInfo.absolutePath());
|
||||
watcher->rawFileModified(fileInfo.absolutePath(), {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <native/FileWatcher/FileWatcher_mac.h>
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* 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 <native/FileWatcher/FileWatcher.h>
|
||||
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
|
||||
struct FolderRootWatch::PlatformImplementation
|
||||
{
|
||||
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
|
||||
HANDLE m_directoryHandle;
|
||||
HANDLE m_ioHandle;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// FolderWatchRoot
|
||||
FolderRootWatch::FolderRootWatch(const QString rootFolder)
|
||||
: m_root(rootFolder)
|
||||
, m_shutdownThreadSignal(false)
|
||||
, m_fileWatcher(nullptr)
|
||||
, m_platformImpl(new PlatformImplementation())
|
||||
{
|
||||
}
|
||||
|
||||
FolderRootWatch::~FolderRootWatch()
|
||||
{
|
||||
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
|
||||
Stop();
|
||||
|
||||
delete m_platformImpl;
|
||||
}
|
||||
|
||||
bool FolderRootWatch::Start()
|
||||
{
|
||||
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
|
||||
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m_shutdownThreadSignal = false;
|
||||
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FolderRootWatch::Stop()
|
||||
{
|
||||
m_shutdownThreadSignal = true;
|
||||
CloseHandle(m_platformImpl->m_ioHandle);
|
||||
m_platformImpl->m_ioHandle = nullptr;
|
||||
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join(); // wait for the thread to finish
|
||||
m_thread = std::thread(); //destroy
|
||||
}
|
||||
CloseHandle(m_platformImpl->m_directoryHandle);
|
||||
m_platformImpl->m_directoryHandle = nullptr;
|
||||
}
|
||||
|
||||
void FolderRootWatch::WatchFolderLoop()
|
||||
{
|
||||
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
|
||||
QString path;
|
||||
OVERLAPPED aOverlapped;
|
||||
LPOVERLAPPED pOverlapped;
|
||||
DWORD dwByteCount;
|
||||
ULONG_PTR ulKey;
|
||||
|
||||
while (!m_shutdownThreadSignal)
|
||||
{
|
||||
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
|
||||
::memset(&aOverlapped, 0, sizeof(aOverlapped));
|
||||
|
||||
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
|
||||
{
|
||||
//wait for up to a second for I/O to signal
|
||||
dwByteCount = 0;
|
||||
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
|
||||
{
|
||||
//if we are signaled to shutdown bypass
|
||||
if (!m_shutdownThreadSignal && ulKey)
|
||||
{
|
||||
if (dwByteCount)
|
||||
{
|
||||
int offset = 0;
|
||||
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
|
||||
do
|
||||
{
|
||||
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
|
||||
|
||||
path.clear();
|
||||
path.append(m_root);
|
||||
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
|
||||
|
||||
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
|
||||
|
||||
switch (pFileNotifyInformation->Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED:
|
||||
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||
ProcessNewFileEvent(file);
|
||||
break;
|
||||
case FILE_ACTION_REMOVED:
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
ProcessDeleteFileEvent(file);
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
ProcessModifyFileEvent(file);
|
||||
break;
|
||||
}
|
||||
|
||||
offset = pFileNotifyInformation->NextEntryOffset;
|
||||
} while (offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
native/FileWatcher/FileWatcher_win.cpp
|
||||
native/FileWatcher/FileWatcher_platform.h
|
||||
native/FileWatcher/FileWatcher_windows.cpp
|
||||
native/FileWatcher/FileWatcher_windows.h
|
||||
native/resource.h
|
||||
)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <native/FileWatcher/FileWatcher_windows.h>
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* 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 <native/FileWatcher/FileWatcher.h>
|
||||
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
|
||||
struct FolderRootWatch::PlatformImplementation
|
||||
{
|
||||
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
|
||||
HANDLE m_directoryHandle;
|
||||
HANDLE m_ioHandle;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// FolderWatchRoot
|
||||
FolderRootWatch::FolderRootWatch(const QString rootFolder)
|
||||
: m_root(rootFolder)
|
||||
, m_shutdownThreadSignal(false)
|
||||
, m_fileWatcher(nullptr)
|
||||
, m_platformImpl(new PlatformImplementation())
|
||||
{
|
||||
}
|
||||
|
||||
FolderRootWatch::~FolderRootWatch()
|
||||
{
|
||||
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
|
||||
Stop();
|
||||
|
||||
delete m_platformImpl;
|
||||
}
|
||||
|
||||
bool FolderRootWatch::Start()
|
||||
{
|
||||
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
|
||||
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m_shutdownThreadSignal = false;
|
||||
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FolderRootWatch::Stop()
|
||||
{
|
||||
m_shutdownThreadSignal = true;
|
||||
CloseHandle(m_platformImpl->m_ioHandle);
|
||||
m_platformImpl->m_ioHandle = nullptr;
|
||||
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join(); // wait for the thread to finish
|
||||
m_thread = std::thread(); //destroy
|
||||
}
|
||||
CloseHandle(m_platformImpl->m_directoryHandle);
|
||||
m_platformImpl->m_directoryHandle = nullptr;
|
||||
}
|
||||
|
||||
void FolderRootWatch::WatchFolderLoop()
|
||||
{
|
||||
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
|
||||
QString path;
|
||||
OVERLAPPED aOverlapped;
|
||||
LPOVERLAPPED pOverlapped;
|
||||
DWORD dwByteCount;
|
||||
ULONG_PTR ulKey;
|
||||
|
||||
while (!m_shutdownThreadSignal)
|
||||
{
|
||||
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
|
||||
::memset(&aOverlapped, 0, sizeof(aOverlapped));
|
||||
|
||||
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
|
||||
{
|
||||
//wait for up to a second for I/O to signal
|
||||
dwByteCount = 0;
|
||||
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
|
||||
{
|
||||
//if we are signaled to shutdown bypass
|
||||
if (!m_shutdownThreadSignal && ulKey)
|
||||
{
|
||||
if (dwByteCount)
|
||||
{
|
||||
int offset = 0;
|
||||
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
|
||||
do
|
||||
{
|
||||
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
|
||||
|
||||
path.clear();
|
||||
path.append(m_root);
|
||||
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
|
||||
|
||||
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
|
||||
|
||||
switch (pFileNotifyInformation->Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED:
|
||||
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||
ProcessNewFileEvent(file);
|
||||
break;
|
||||
case FILE_ACTION_REMOVED:
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
ProcessDeleteFileEvent(file);
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
ProcessModifyFileEvent(file);
|
||||
break;
|
||||
}
|
||||
|
||||
offset = pFileNotifyInformation->NextEntryOffset;
|
||||
} while (offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 <AzCore/std/tuple.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
#include <native/FileWatcher/FileWatcher.h>
|
||||
#include <native/FileWatcher/FileWatcher_platform.h>
|
||||
#include <QDir>
|
||||
|
||||
bool FileWatcher::PlatformStart()
|
||||
{
|
||||
m_shutdownThreadSignal = false;
|
||||
|
||||
bool allSucceeded = true;
|
||||
for (const auto& [directory, recursive] : m_folderWatchRoots)
|
||||
{
|
||||
if (QDir(directory).exists())
|
||||
{
|
||||
allSucceeded &= m_platformImpl->AddWatchFolder(directory, recursive);
|
||||
}
|
||||
}
|
||||
return allSucceeded;
|
||||
}
|
||||
|
||||
bool FileWatcher::PlatformImplementation::AddWatchFolder(QString root, bool recursive)
|
||||
{
|
||||
HandleUniquePtr directoryHandle{::CreateFileW(
|
||||
root.toStdWString().data(),
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||
nullptr
|
||||
)};
|
||||
|
||||
if (directoryHandle.get() == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
AZ_Warning("FileWatcher", false, "Failed to start watching %s", root.toUtf8().constData());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Associate this file handle with our existing io completion port handle
|
||||
if (!::CreateIoCompletionPort(directoryHandle.get(), m_ioHandle.get(), /*CompletionKey =*/ static_cast<ULONG_PTR>(PlatformImplementation::EventType::FileRead), 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto id = AZStd::make_unique<OVERLAPPED>();
|
||||
auto* idp = id.get();
|
||||
const auto& [folderWatch, inserted] = m_folderRootWatches.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(idp),
|
||||
AZStd::forward_as_tuple(AZStd::move(id), AZStd::move(directoryHandle), root, recursive));
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return folderWatch->second.ReadChanges();
|
||||
}
|
||||
|
||||
bool FileWatcher::PlatformImplementation::FolderRootWatch::ReadChanges()
|
||||
{
|
||||
// Register to get directory change notifications for our directory handle
|
||||
return ::ReadDirectoryChangesW(
|
||||
m_directoryHandle.get(),
|
||||
&m_fileNotifyInformationList,
|
||||
sizeof(m_fileNotifyInformationList),
|
||||
m_recursive,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME,
|
||||
nullptr,
|
||||
m_overlapped.get(),
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void FileWatcher::PlatformStop()
|
||||
{
|
||||
m_shutdownThreadSignal = true;
|
||||
|
||||
// Send a special signal to the child thread, that is blocked in a GetQueuedCompletionStatus call, with a completion
|
||||
// key set to Shutdown. The child thread will stop its processing when it receives this value for the completion key
|
||||
PostQueuedCompletionStatus(m_platformImpl->m_ioHandle.get(), 0, /*CompletionKey =*/ static_cast<ULONG_PTR>(PlatformImplementation::EventType::Shutdown), nullptr);
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join(); // wait for the thread to finish
|
||||
}
|
||||
}
|
||||
|
||||
void FileWatcher::WatchFolderLoop()
|
||||
{
|
||||
LPOVERLAPPED directoryId = nullptr;
|
||||
ULONG_PTR completionKey = 0;
|
||||
|
||||
while (!m_shutdownThreadSignal)
|
||||
{
|
||||
DWORD dwByteCount = 0;
|
||||
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle.get(), &dwByteCount, &completionKey, &directoryId, INFINITE))
|
||||
{
|
||||
if (m_shutdownThreadSignal || completionKey == static_cast<ULONG_PTR>(PlatformImplementation::EventType::Shutdown))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (dwByteCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto foundFolderRoot = m_platformImpl->m_folderRootWatches.find(directoryId);
|
||||
if (foundFolderRoot == end(m_platformImpl->m_folderRootWatches))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PlatformImplementation::FolderRootWatch& folderRoot = foundFolderRoot->second;
|
||||
|
||||
// Initialize offset to 1 to ensure that the first iteration is always processed
|
||||
DWORD offset = 1;
|
||||
for (
|
||||
const FILE_NOTIFY_INFORMATION* pFileNotifyInformation = reinterpret_cast<const FILE_NOTIFY_INFORMATION*>(&folderRoot.m_fileNotifyInformationList);
|
||||
offset;
|
||||
pFileNotifyInformation = reinterpret_cast<const FILE_NOTIFY_INFORMATION*>(reinterpret_cast<const char*>(pFileNotifyInformation) + offset)
|
||||
){
|
||||
const QString file = QDir::toNativeSeparators(QDir(folderRoot.m_directoryRoot)
|
||||
.filePath(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2)));
|
||||
|
||||
switch (pFileNotifyInformation->Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED:
|
||||
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||
rawFileAdded(file, {});
|
||||
break;
|
||||
case FILE_ACTION_REMOVED:
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
rawFileRemoved(file, {});
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
rawFileModified(file, {});
|
||||
break;
|
||||
}
|
||||
|
||||
offset = pFileNotifyInformation->NextEntryOffset;
|
||||
}
|
||||
|
||||
folderRoot.ReadChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/typetraits/aligned_storage.h>
|
||||
#include <AzCore/std/typetraits/remove_pointer.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
#include <native/FileWatcher/FileWatcher.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
|
||||
struct HandleDeleter
|
||||
{
|
||||
void operator()(HANDLE handle)
|
||||
{
|
||||
if (handle && handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using HandleUniquePtr = AZStd::unique_ptr<AZStd::remove_pointer_t<HANDLE>, HandleDeleter>;
|
||||
|
||||
class FileWatcher::PlatformImplementation
|
||||
{
|
||||
public:
|
||||
bool AddWatchFolder(QString folder, bool recursive);
|
||||
|
||||
struct FolderRootWatch
|
||||
{
|
||||
FolderRootWatch(AZStd::unique_ptr<OVERLAPPED>&& overlapped, HandleUniquePtr&& directoryHandle, QString root, bool recursive)
|
||||
: m_overlapped(AZStd::move(overlapped))
|
||||
, m_directoryHandle(AZStd::move(directoryHandle))
|
||||
, m_directoryRoot(AZStd::move(root))
|
||||
, m_recursive(recursive)
|
||||
{
|
||||
}
|
||||
|
||||
bool ReadChanges();
|
||||
|
||||
AZStd::unique_ptr<OVERLAPPED> m_overlapped; // Identifies this root watch
|
||||
HandleUniquePtr m_directoryHandle;
|
||||
QString m_directoryRoot;
|
||||
bool m_recursive;
|
||||
AZStd::aligned_storage_t<64 * 1024, sizeof(DWORD)> m_fileNotifyInformationList{};
|
||||
};
|
||||
|
||||
enum class EventType
|
||||
{
|
||||
FileRead,
|
||||
Shutdown
|
||||
};
|
||||
|
||||
AZStd::unordered_map<LPOVERLAPPED, FolderRootWatch> m_folderRootWatches;
|
||||
|
||||
HandleUniquePtr m_ioHandle{CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, /*CompletionKey =*/ static_cast<ULONG_PTR>(EventType::FileRead), 1)};
|
||||
};
|
||||
@@ -44,7 +44,6 @@ set(FILES
|
||||
native/FileProcessor/FileProcessor.h
|
||||
native/FileWatcher/FileWatcher.cpp
|
||||
native/FileWatcher/FileWatcher.h
|
||||
native/FileWatcher/FileWatcherAPI.h
|
||||
native/InternalBuilders/SettingsRegistryBuilder.cpp
|
||||
native/InternalBuilders/SettingsRegistryBuilder.h
|
||||
native/resourcecompiler/JobsModel.cpp
|
||||
@@ -63,13 +62,6 @@ set(FILES
|
||||
native/resourcecompiler/RCJobSortFilterProxyModel.h
|
||||
native/resourcecompiler/RCQueueSortModel.cpp
|
||||
native/resourcecompiler/RCQueueSortModel.h
|
||||
native/shadercompiler/shadercompilerjob.cpp
|
||||
native/shadercompiler/shadercompilerjob.h
|
||||
native/shadercompiler/shadercompilerManager.cpp
|
||||
native/shadercompiler/shadercompilerManager.h
|
||||
native/shadercompiler/shadercompilerMessages.h
|
||||
native/shadercompiler/shadercompilerModel.cpp
|
||||
native/shadercompiler/shadercompilerModel.h
|
||||
native/utilities/ApplicationManagerAPI.h
|
||||
native/utilities/ApplicationManager.cpp
|
||||
native/utilities/ApplicationManager.h
|
||||
|
||||
@@ -67,8 +67,6 @@ set(FILES
|
||||
native/unittests/PlatformConfigurationUnitTests.h
|
||||
native/unittests/RCcontrollerUnitTests.cpp
|
||||
native/unittests/RCcontrollerUnitTests.h
|
||||
native/unittests/ShaderCompilerUnitTests.cpp
|
||||
native/unittests/ShaderCompilerUnitTests.h
|
||||
native/unittests/UnitTestRunner.cpp
|
||||
native/unittests/UnitTestRunner.h
|
||||
native/unittests/UtilitiesUnitTests.cpp
|
||||
|
||||
@@ -6,154 +6,119 @@
|
||||
*
|
||||
*/
|
||||
#include "FileWatcher.h"
|
||||
#include "AzCore/std/containers/vector.h"
|
||||
#include <native/assetprocessor.h>
|
||||
#include <native/FileWatcher/FileWatcher_platform.h>
|
||||
#include <QFileInfo>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// FolderWatchRoot
|
||||
void FolderRootWatch::ProcessNewFileEvent(const QString& file)
|
||||
//! IsSubfolder(folderA, folderB)
|
||||
//! returns whether folderA is a subfolder of folderB
|
||||
//! assumptions: absolute paths
|
||||
static bool IsSubfolder(const QString& folderA, const QString& folderB)
|
||||
{
|
||||
FileChangeInfo info;
|
||||
info.m_action = FileAction::FileAction_Added;
|
||||
info.m_filePath = file;
|
||||
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
|
||||
Q_ASSERT(invoked);
|
||||
}
|
||||
// lets avoid allocating or messing with memory - this is a MAJOR hotspot as it is called for any file change even in the cache!
|
||||
if (folderA.length() <= folderB.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void FolderRootWatch::ProcessDeleteFileEvent(const QString& file)
|
||||
{
|
||||
FileChangeInfo info;
|
||||
info.m_action = FileAction::FileAction_Removed;
|
||||
info.m_filePath = file;
|
||||
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
|
||||
Q_ASSERT(invoked);
|
||||
}
|
||||
using AZStd::begin;
|
||||
using AZStd::end;
|
||||
|
||||
void FolderRootWatch::ProcessModifyFileEvent(const QString& file)
|
||||
{
|
||||
FileChangeInfo info;
|
||||
info.m_action = FileAction::FileAction_Modified;
|
||||
info.m_filePath = file;
|
||||
const bool invoked = QMetaObject::invokeMethod(m_fileWatcher, "AnyFileChange", Qt::QueuedConnection, Q_ARG(FileChangeInfo, info));
|
||||
Q_ASSERT(invoked);
|
||||
constexpr auto isSlash = [](const QChar c) constexpr
|
||||
{
|
||||
return c == AZ::IO::WindowsPathSeparator || c == AZ::IO::PosixPathSeparator;
|
||||
};
|
||||
|
||||
const auto firstPathSeparator = AZStd::find_if(begin(folderB), end(folderB), [&isSlash](const QChar c)
|
||||
{
|
||||
return isSlash(c);
|
||||
});
|
||||
|
||||
// Follow the convention used by AZ::IO::Path, and use a case-sensitive comparison on Posix paths
|
||||
const bool useCaseSensitiveCompare = (firstPathSeparator == end(folderB)) ? true : (*firstPathSeparator == AZ::IO::PosixPathSeparator);
|
||||
|
||||
return AZStd::equal(begin(folderB), end(folderB), begin(folderA), [isSlash, useCaseSensitiveCompare](const QChar charAtB, const QChar charAtA)
|
||||
{
|
||||
if (isSlash(charAtA))
|
||||
{
|
||||
return isSlash(charAtB);
|
||||
}
|
||||
if (useCaseSensitiveCompare)
|
||||
{
|
||||
return charAtA == charAtB;
|
||||
}
|
||||
return charAtA.toLower() == charAtB.toLower();
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// FileWatcher
|
||||
FileWatcher::FileWatcher()
|
||||
: m_nextHandle(0)
|
||||
: m_platformImpl(AZStd::make_unique<PlatformImplementation>())
|
||||
{
|
||||
qRegisterMetaType<FileChangeInfo>("FileChangeInfo");
|
||||
auto makeFilter = [this](auto signal)
|
||||
{
|
||||
return [this, signal](QString path)
|
||||
{
|
||||
const auto foundWatchRoot = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [path](const WatchRoot& watchRoot)
|
||||
{
|
||||
return Filter(path, watchRoot);
|
||||
});
|
||||
if (foundWatchRoot == end(m_folderWatchRoots))
|
||||
{
|
||||
return;
|
||||
}
|
||||
AZStd::invoke(signal, this, path);
|
||||
};
|
||||
};
|
||||
|
||||
// The rawFileAdded signals are emitted by the watcher thread. Use a queued
|
||||
// connection so that the consumers of the notification process the
|
||||
// notification on the main thread.
|
||||
connect(this, &FileWatcher::rawFileAdded, this, makeFilter(&FileWatcher::fileAdded), Qt::QueuedConnection);
|
||||
connect(this, &FileWatcher::rawFileRemoved, this, makeFilter(&FileWatcher::fileRemoved), Qt::QueuedConnection);
|
||||
connect(this, &FileWatcher::rawFileModified, this, makeFilter(&FileWatcher::fileModified), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
FileWatcher::~FileWatcher()
|
||||
{
|
||||
disconnect();
|
||||
StopWatching();
|
||||
}
|
||||
|
||||
int FileWatcher::AddFolderWatch(FolderWatchBase* pFolderWatch)
|
||||
void FileWatcher::AddFolderWatch(QString directory, bool recursive)
|
||||
{
|
||||
if (!pFolderWatch)
|
||||
// Search for an already monitored root that is a parent of `directory`,
|
||||
// that is already watching subdirectories recursively
|
||||
const auto found = AZStd::find_if(begin(m_folderWatchRoots), end(m_folderWatchRoots), [directory](const WatchRoot& root)
|
||||
{
|
||||
return -1;
|
||||
return root.m_recursive && IsSubfolder(directory, root.m_directory);
|
||||
});
|
||||
|
||||
if (found != end(m_folderWatchRoots))
|
||||
{
|
||||
// This directory is already watched
|
||||
return;
|
||||
}
|
||||
|
||||
FolderRootWatch* pFolderRootWatch = nullptr;
|
||||
//create a new root and start listening for changes
|
||||
m_folderWatchRoots.push_back({directory, recursive});
|
||||
|
||||
//see if this a sub folder of an already watched root
|
||||
for (auto rootsIter = m_folderWatchRoots.begin(); !pFolderRootWatch && rootsIter != m_folderWatchRoots.end(); ++rootsIter)
|
||||
//since we created a new root, see if the new root is a super folder
|
||||
//of other roots, if it is then then fold those roots into the new super root
|
||||
if (recursive)
|
||||
{
|
||||
if (FolderWatchBase::IsSubfolder(pFolderWatch->m_folder, (*rootsIter)->m_root))
|
||||
AZStd::erase_if(m_folderWatchRoots, [directory](const WatchRoot& root)
|
||||
{
|
||||
pFolderRootWatch = *rootsIter;
|
||||
}
|
||||
return IsSubfolder(root.m_directory, directory);
|
||||
});
|
||||
}
|
||||
|
||||
bool bCreatedNewRoot = false;
|
||||
//if its not a sub folder
|
||||
if (!pFolderRootWatch)
|
||||
{
|
||||
//create a new root and start listening for changes
|
||||
pFolderRootWatch = new FolderRootWatch(pFolderWatch->m_folder);
|
||||
|
||||
//make sure the folder watcher(s) get deleted before this
|
||||
pFolderRootWatch->setParent(this);
|
||||
bCreatedNewRoot = true;
|
||||
}
|
||||
|
||||
pFolderRootWatch->m_fileWatcher = this;
|
||||
QObject::connect(this, &FileWatcher::AnyFileChange, pFolderWatch, &FolderWatchBase::OnAnyFileChange);
|
||||
|
||||
if (bCreatedNewRoot)
|
||||
{
|
||||
if (m_startedWatching)
|
||||
{
|
||||
pFolderRootWatch->Start();
|
||||
}
|
||||
|
||||
//since we created a new root, see if the new root is a super folder
|
||||
//of other roots, if it is then then fold those roots into the new super root
|
||||
for (auto rootsIter = m_folderWatchRoots.begin(); rootsIter != m_folderWatchRoots.end(); )
|
||||
{
|
||||
if (FolderWatchBase::IsSubfolder((*rootsIter)->m_root, pFolderWatch->m_folder))
|
||||
{
|
||||
//union the sub folder map over to the new root
|
||||
pFolderRootWatch->m_subFolderWatchesMap.insert((*rootsIter)->m_subFolderWatchesMap);
|
||||
|
||||
//clear the old root sub folders map so they don't get deleted when we
|
||||
//delete the old root as they are now pointed to by the new root
|
||||
(*rootsIter)->m_subFolderWatchesMap.clear();
|
||||
|
||||
//delete the empty old root, deleting a root will call Stop()
|
||||
//automatically which kills the thread
|
||||
delete *rootsIter;
|
||||
|
||||
//remove the old root pointer form the watched list
|
||||
rootsIter = m_folderWatchRoots.erase(rootsIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++rootsIter;
|
||||
}
|
||||
}
|
||||
|
||||
//add the new root to the watched roots
|
||||
m_folderWatchRoots.push_back(pFolderRootWatch);
|
||||
}
|
||||
|
||||
//add to the root
|
||||
pFolderRootWatch->m_subFolderWatchesMap.insert(m_nextHandle, pFolderWatch);
|
||||
|
||||
m_nextHandle++;
|
||||
|
||||
return m_nextHandle - 1;
|
||||
}
|
||||
|
||||
void FileWatcher::RemoveFolderWatch(int handle)
|
||||
void FileWatcher::ClearFolderWatches()
|
||||
{
|
||||
for (auto rootsIter = m_folderWatchRoots.begin(); rootsIter != m_folderWatchRoots.end(); )
|
||||
{
|
||||
//find an element by the handle
|
||||
auto foundIter = (*rootsIter)->m_subFolderWatchesMap.find(handle);
|
||||
if (foundIter != (*rootsIter)->m_subFolderWatchesMap.end())
|
||||
{
|
||||
//remove the element
|
||||
(*rootsIter)->m_subFolderWatchesMap.erase(foundIter);
|
||||
|
||||
//we removed a folder watch, if it's empty then there is no reason to keep watching it.
|
||||
if ((*rootsIter)->m_subFolderWatchesMap.empty())
|
||||
{
|
||||
delete(*rootsIter);
|
||||
rootsIter = m_folderWatchRoots.erase(rootsIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++rootsIter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++rootsIter;
|
||||
}
|
||||
}
|
||||
m_folderWatchRoots.clear();
|
||||
}
|
||||
|
||||
void FileWatcher::StartWatching()
|
||||
@@ -164,12 +129,18 @@ void FileWatcher::StartWatching()
|
||||
return;
|
||||
}
|
||||
|
||||
for (FolderRootWatch* root : m_folderWatchRoots)
|
||||
if (PlatformStart())
|
||||
{
|
||||
root->Start();
|
||||
m_thread = AZStd::thread({/*.name=*/ "AssetProcessor FileWatcher thread"}, [this]{
|
||||
WatchFolderLoop();
|
||||
});
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring started.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring failed to start.\n");
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "File Change Monitoring started.\n");
|
||||
m_startedWatching = true;
|
||||
}
|
||||
|
||||
@@ -177,17 +148,35 @@ void FileWatcher::StopWatching()
|
||||
{
|
||||
if (!m_startedWatching)
|
||||
{
|
||||
AZ_Warning("FileWatcher", false, "StartWatching() called when is not watching for file changes.");
|
||||
AZ_Warning("FileWatcher", false, "StopWatching() called when is not watching for file changes.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (FolderRootWatch* root : m_folderWatchRoots)
|
||||
{
|
||||
root->Stop();
|
||||
}
|
||||
PlatformStop();
|
||||
|
||||
m_startedWatching = false;
|
||||
}
|
||||
|
||||
#include "native/FileWatcher/moc_FileWatcher.cpp"
|
||||
#include "native/FileWatcher/moc_FileWatcherAPI.cpp"
|
||||
bool FileWatcher::Filter(QString path, const WatchRoot& watchRoot)
|
||||
{
|
||||
if (!IsSubfolder(path, watchRoot.m_directory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!watchRoot.m_recursive)
|
||||
{
|
||||
// filter out subtrees too.
|
||||
QStringRef subRef = path.rightRef(path.length() - watchRoot.m_directory.length());
|
||||
if ((subRef.indexOf('/') != -1) || (subRef.indexOf('\\') != -1))
|
||||
{
|
||||
return false; // filter this out.
|
||||
}
|
||||
|
||||
// we don't care about subdirs. IsDir is more expensive so we do it after the above filter.
|
||||
if (QFileInfo(path).isDir())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,62 +5,21 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef FILEWATCHER_COMPONENT_H
|
||||
#define FILEWATCHER_COMPONENT_H
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "FileWatcherAPI.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
class FileWatcher;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FolderRootWatch
|
||||
/*! Class used for holding a point in the files system from which file changes are tracked.
|
||||
* */
|
||||
class FolderRootWatch
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class FileWatcher;
|
||||
public:
|
||||
FolderRootWatch(const QString rootFolder);
|
||||
virtual ~FolderRootWatch();
|
||||
|
||||
void ProcessNewFileEvent(const QString& file);
|
||||
void ProcessDeleteFileEvent(const QString& file);
|
||||
void ProcessModifyFileEvent(const QString& file);
|
||||
void ProcessRenameFileEvent(const QString& fileOld, const QString& fileNew);
|
||||
|
||||
public Q_SLOTS:
|
||||
bool Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void WatchFolderLoop();
|
||||
|
||||
private:
|
||||
std::thread m_thread;
|
||||
QString m_root;
|
||||
QMap<int, FolderWatchBase*> m_subFolderWatchesMap;
|
||||
volatile bool m_shutdownThreadSignal;
|
||||
FileWatcher* m_fileWatcher;
|
||||
|
||||
// Can't use unique_ptr because this is a QObject and Qt's magic sauce is
|
||||
// unable to determine the size of the unique_ptr and so fails to compile
|
||||
struct PlatformImplementation;
|
||||
PlatformImplementation* m_platformImpl;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FileWatcher
|
||||
/*! Class that handles creation and deletion of FolderRootWatches based on
|
||||
@@ -73,23 +32,47 @@ class FileWatcher
|
||||
|
||||
public:
|
||||
FileWatcher();
|
||||
virtual ~FileWatcher();
|
||||
~FileWatcher() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual int AddFolderWatch(FolderWatchBase* pFolderWatch);
|
||||
virtual void RemoveFolderWatch(int handle);
|
||||
void AddFolderWatch(QString directory, bool recursive = true);
|
||||
void ClearFolderWatches();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void StartWatching();
|
||||
void StopWatching();
|
||||
|
||||
Q_SIGNALS:
|
||||
void AnyFileChange(FileChangeInfo info);
|
||||
// These signals are emitted when a file under a watched path changes
|
||||
void fileAdded(QString filePath);
|
||||
void fileRemoved(QString filePath);
|
||||
void fileModified(QString filePath);
|
||||
|
||||
// These signals are emitted by the platform implementations when files
|
||||
// change. Some platforms' file watch APIs do not support non-recursive
|
||||
// watches, so the signals are filtered before being forwarded to the
|
||||
// non-"raw" fileAdded/Removed/Modified signals above.
|
||||
void rawFileAdded(QString filePath, QPrivateSignal);
|
||||
void rawFileRemoved(QString filePath, QPrivateSignal);
|
||||
void rawFileModified(QString filePath, QPrivateSignal);
|
||||
|
||||
private:
|
||||
int m_nextHandle;
|
||||
AZStd::vector<FolderRootWatch*> m_folderWatchRoots;
|
||||
bool m_startedWatching = false;
|
||||
};
|
||||
bool PlatformStart();
|
||||
void PlatformStop();
|
||||
void WatchFolderLoop();
|
||||
|
||||
#endif//FILEWATCHER_COMPONENT_H
|
||||
class PlatformImplementation;
|
||||
friend class PlatformImplementation;
|
||||
struct WatchRoot
|
||||
{
|
||||
QString m_directory;
|
||||
bool m_recursive;
|
||||
};
|
||||
static bool Filter(QString path, const WatchRoot& watchRoot);
|
||||
|
||||
AZStd::unique_ptr<PlatformImplementation> m_platformImpl;
|
||||
AZStd::vector<WatchRoot> m_folderWatchRoots;
|
||||
AZStd::thread m_thread;
|
||||
bool m_startedWatching = false;
|
||||
AZStd::atomic_bool m_shutdownThreadSignal = false;
|
||||
};
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef FILEWATCHERAPI_H
|
||||
#define FILEWATCHERAPI_H
|
||||
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FileAction
|
||||
/*! Enum for which file changes are tracked.
|
||||
* */
|
||||
enum FileAction
|
||||
{
|
||||
FileAction_None = 0x00,
|
||||
FileAction_Added = 0x01,
|
||||
FileAction_Removed = 0x02,
|
||||
FileAction_Modified = 0x04,
|
||||
FileAction_Any = 0xFF,
|
||||
};
|
||||
inline FileAction operator | (FileAction a, FileAction b)
|
||||
{
|
||||
return static_cast<FileAction>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
inline FileAction operator & (FileAction a, FileAction b)
|
||||
{
|
||||
return static_cast<FileAction>(static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FileChangeInfo
|
||||
/*! Struct for passing along information about file changes.
|
||||
* */
|
||||
struct FileChangeInfo
|
||||
{
|
||||
FileChangeInfo()
|
||||
: m_action(FileAction::FileAction_None)
|
||||
{}
|
||||
FileChangeInfo(const FileChangeInfo& rhs)
|
||||
: m_action(rhs.m_action)
|
||||
, m_filePath(rhs.m_filePath)
|
||||
, m_filePathOld(rhs.m_filePathOld)
|
||||
{
|
||||
}
|
||||
|
||||
FileAction m_action;
|
||||
QString m_filePath;
|
||||
QString m_filePathOld;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FileChangeInfo)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FolderWatchBase
|
||||
/*! Class for filtering file changes generated from a root watch. Define your own
|
||||
*! custom filtering by deriving from this base class and implement your own
|
||||
*! custom code for what to do when receiving a file change notification.
|
||||
* */
|
||||
class FolderWatchBase
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FolderWatchBase(const QString strFolder, bool bWatchSubtree = true, FileAction fileAction = FileAction::FileAction_Any)
|
||||
: m_folder(strFolder)
|
||||
, m_watchSubtree(bWatchSubtree)
|
||||
, m_fileAction(fileAction)
|
||||
{
|
||||
m_folder = QDir::toNativeSeparators(QDir::cleanPath(m_folder) + "/");
|
||||
}
|
||||
|
||||
//! IsSubfolder(folderA, folderB)
|
||||
//! returns whether folderA is a subfolder of folderB
|
||||
//! assumptions: absolute paths, case insensitive
|
||||
static bool IsSubfolder(const QString& folderA, const QString& folderB)
|
||||
{
|
||||
// lets avoid allocating or messing with memory - this is a MAJOR hotspot as it is called for any file change even in the cache!
|
||||
int sizeB = folderB.length();
|
||||
int sizeA = folderA.length();
|
||||
|
||||
if (sizeA <= sizeB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QChar slash1 = QChar('\\');
|
||||
QChar slash2 = QChar('/');
|
||||
int posA = 0;
|
||||
|
||||
// A is going to be the longer one, so use B:
|
||||
for (int idx = 0; idx < sizeB; ++idx)
|
||||
{
|
||||
QChar charAtA = folderA.at(posA);
|
||||
QChar charAtB = folderB.at(idx);
|
||||
|
||||
if ((charAtB == slash1) || (charAtB == slash2))
|
||||
{
|
||||
if ((charAtA != slash1) && (charAtA != slash2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++posA;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (charAtA.toLower() != charAtB.toLower())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++posA;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString m_folder;
|
||||
bool m_watchSubtree;
|
||||
FileAction m_fileAction;
|
||||
|
||||
public Q_SLOTS:
|
||||
void OnAnyFileChange(FileChangeInfo info)
|
||||
{
|
||||
//if they set a file action then respect it by rejecting non matching file actions
|
||||
if (info.m_action & m_fileAction)
|
||||
{
|
||||
//is the file is in the folder or subtree (if specified) then call OnFileChange
|
||||
|
||||
if (FolderWatchBase::IsSubfolder(info.m_filePath, m_folder))
|
||||
{
|
||||
OnFileChange(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnFileChange(const FileChangeInfo& info) = 0;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! FolderWatchCallbackEx
|
||||
/*! Class implements a more complex filtering that can optionally filter for file
|
||||
*! extension and call different callback for different kinds of file changes
|
||||
*! generated from a root watch.
|
||||
*! Notes:
|
||||
*! - empty extension "" catches all file changes
|
||||
*! - extension should not include the leading "."
|
||||
* */
|
||||
class FolderWatchCallbackEx
|
||||
: public FolderWatchBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FolderWatchCallbackEx(const QString strFolder, const QString extension, bool bWatchSubtree)
|
||||
: FolderWatchBase(strFolder, bWatchSubtree)
|
||||
, m_extension(extension)
|
||||
{
|
||||
}
|
||||
|
||||
QString m_extension;
|
||||
|
||||
//on file change call the change callback if passes extension then route
|
||||
//to specific file action type callback
|
||||
virtual void OnFileChange(const FileChangeInfo& info)
|
||||
{
|
||||
//if they set an extension to watch for only let matching extensions through
|
||||
QFileInfo fileInfo(info.m_filePath);
|
||||
|
||||
if (!m_watchSubtree)
|
||||
{
|
||||
// filter out subtrees too.
|
||||
QStringRef subRef = info.m_filePath.rightRef(info.m_filePath.length() - m_folder.length());
|
||||
if ((subRef.indexOf('/') != -1) || (subRef.indexOf('\\') != -1))
|
||||
{
|
||||
return; // filter this out.
|
||||
}
|
||||
|
||||
// we don't care about subdirs. IsDir is more expensive so we do it after the above filter.
|
||||
if (fileInfo.isDir())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_extension.isEmpty() || fileInfo.completeSuffix().compare(m_extension, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
if (info.m_action & FileAction::FileAction_Any)
|
||||
{
|
||||
Q_EMIT fileChange(info);
|
||||
}
|
||||
|
||||
if (info.m_action & FileAction::FileAction_Added)
|
||||
{
|
||||
Q_EMIT fileAdded(info.m_filePath);
|
||||
}
|
||||
|
||||
if (info.m_action & FileAction::FileAction_Removed)
|
||||
{
|
||||
Q_EMIT fileRemoved(info.m_filePath);
|
||||
}
|
||||
|
||||
if (info.m_action & FileAction::FileAction_Modified)
|
||||
{
|
||||
Q_EMIT fileModified(info.m_filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void fileChange(FileChangeInfo info);
|
||||
void fileAdded(QString filePath);
|
||||
void fileRemoved(QString filePath);
|
||||
void fileModified(QString filePath);
|
||||
};
|
||||
|
||||
#endif//FILEWATCHERAPI_H
|
||||
@@ -85,7 +85,7 @@ namespace AssetProcessor
|
||||
|
||||
enum AssetCatalogStatus
|
||||
{
|
||||
RequiresSaving,
|
||||
RequiresSaving,
|
||||
UpToDate
|
||||
};
|
||||
|
||||
@@ -213,11 +213,11 @@ namespace AssetProcessor
|
||||
|
||||
bool m_critical = false;
|
||||
int m_priority = -1;
|
||||
// indicates whether we need to check the server first for the outputs of this job
|
||||
// indicates whether we need to check the server first for the outputs of this job
|
||||
// before we start processing locally
|
||||
bool m_checkServer = false;
|
||||
|
||||
// Indicates whether this job needs to be processed irrespective of whether its fingerprint got modified or not.
|
||||
|
||||
// Indicates whether this job needs to be processed irrespective of whether its fingerprint got modified or not.
|
||||
bool m_autoProcessJob = false;
|
||||
|
||||
AssetBuilderSDK::AssetBuilderDesc m_assetBuilderDesc;
|
||||
@@ -251,9 +251,9 @@ namespace AssetProcessor
|
||||
|
||||
JobDetails() = default;
|
||||
};
|
||||
|
||||
//! JobDesc struct is used for identifying jobs that need to be processed again
|
||||
//! because of job dependency declared on them by other jobs
|
||||
|
||||
//! JobDesc struct is used for identifying jobs that need to be processed again
|
||||
//! because of job dependency declared on them by other jobs
|
||||
struct JobDesc
|
||||
{
|
||||
AZStd::string m_databaseSourceName;
|
||||
@@ -283,7 +283,7 @@ namespace AssetProcessor
|
||||
}
|
||||
};
|
||||
|
||||
//! JobIndentifier is an internal structure that store all the data that can uniquely identify a job
|
||||
//! JobIndentifier is an internal structure that store all the data that can uniquely identify a job
|
||||
struct JobIndentifier
|
||||
{
|
||||
JobDesc m_jobDesc;
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 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 "shadercompilerManager.h"
|
||||
#include "shadercompilerjob.h"
|
||||
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "native/utilities/assetUtils.h"
|
||||
|
||||
ShaderCompilerManager::ShaderCompilerManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_isUnitTesting(false)
|
||||
, m_numberOfJobsStarted(0)
|
||||
, m_numberOfJobsEnded(0)
|
||||
, m_numberOfErrors(0)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderCompilerManager::~ShaderCompilerManager()
|
||||
{
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::process(unsigned int connID, unsigned int type, unsigned int serial, QByteArray payload)
|
||||
{
|
||||
(void)type;
|
||||
(void)serial;
|
||||
Q_ASSERT(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest") == type);
|
||||
decodeShaderCompilerRequest(connID, payload);
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::decodeShaderCompilerRequest(unsigned int connID, QByteArray payload)
|
||||
{
|
||||
if (payload.length() < sizeof(unsigned int) + sizeof(unsigned int) + 2 + sizeof(unsigned short))
|
||||
{
|
||||
QString error = "Payload size is too small";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* data_end = reinterpret_cast<unsigned char*>(payload.data() + payload.size());
|
||||
unsigned int* requestId = reinterpret_cast<unsigned int*>(data_end - sizeof(unsigned int));
|
||||
unsigned int* serverListSizePtr = reinterpret_cast<unsigned int*>(data_end - sizeof(unsigned int) - sizeof(unsigned int));
|
||||
unsigned short* serverPortPtr = reinterpret_cast<unsigned short*>(data_end - sizeof(unsigned int) - sizeof(unsigned int) - sizeof(unsigned short));
|
||||
|
||||
ShaderCompilerRequestMessage msg;
|
||||
QString error;
|
||||
|
||||
msg.requestId = *requestId;
|
||||
msg.serverListSize = *serverListSizePtr;
|
||||
msg.serverPort = *serverPortPtr;
|
||||
if ((msg.serverListSize <= 0) || (msg.serverListSize > 100000))
|
||||
{
|
||||
error = "Shader Compiler Server List is wrong";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
return;
|
||||
}
|
||||
if (msg.serverPort == 0)
|
||||
{
|
||||
error = "Shader Compiler port is wrong";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
return;
|
||||
}
|
||||
|
||||
char* position_of_first_null = reinterpret_cast<char*>(serverPortPtr) - 1;// -1 for null
|
||||
if ((*position_of_first_null) != '\0')
|
||||
{
|
||||
error = "Shader Compiler payload is corrupt,position is not null";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
return;
|
||||
}
|
||||
char* beginning_of_serverList = position_of_first_null - msg.serverListSize;
|
||||
char* position_of_second_null = beginning_of_serverList - 1;//-1 for null
|
||||
|
||||
if ((*position_of_second_null) != '\0')
|
||||
{
|
||||
error = "Shader Compiler payload is corrupt,position is not null";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int originalPayloadSize = static_cast<unsigned int>(payload.size()) - sizeof(unsigned int) - sizeof(unsigned int) - sizeof(unsigned short) - static_cast<unsigned int>(msg.serverListSize) - 2;
|
||||
msg.serverList = beginning_of_serverList;
|
||||
msg.originalPayload.insert(0, payload.data(), static_cast<unsigned int>(originalPayloadSize));
|
||||
ShaderCompilerJob* shaderCompilerJob = new ShaderCompilerJob();
|
||||
shaderCompilerJob->initialize(this, msg);
|
||||
shaderCompilerJob->setIsUnitTesting(m_isUnitTesting);
|
||||
m_shaderCompilerJobMap[msg.requestId] = connID;
|
||||
shaderCompilerJob->setAutoDelete(true);
|
||||
QThreadPool* threadPool = QThreadPool::globalInstance();
|
||||
threadPool->start(shaderCompilerJob);
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::OnShaderCompilerJobComplete(QByteArray payload, unsigned int requestId)
|
||||
{
|
||||
auto iterator = m_shaderCompilerJobMap.find(requestId);
|
||||
if (iterator != m_shaderCompilerJobMap.end())
|
||||
{
|
||||
sendResponse(iterator.value(), AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyResponse"), 0, payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString error = "Shader Compiler cannot find the connection id";
|
||||
AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
|
||||
emit sendErrorMessage(error);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::sendResponse(unsigned int connId, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload)
|
||||
{
|
||||
EBUS_EVENT_ID(connId, AssetProcessor::ConnectionBus, SendRaw, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyResponse"), 0, payload);
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::shaderCompilerError(QString errorMessage, QString server, QString timestamp, QString payload)
|
||||
{
|
||||
m_numberOfErrors++;
|
||||
emit numberOfErrorsChanged();
|
||||
emit sendErrorMessageFromShaderJob(errorMessage, server, timestamp, payload);
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::jobStarted()
|
||||
{
|
||||
m_numberOfJobsStarted++;
|
||||
emit numberOfJobsStartedChanged();
|
||||
}
|
||||
|
||||
void ShaderCompilerManager::jobEnded()
|
||||
{
|
||||
m_numberOfJobsEnded++;
|
||||
numberOfJobsEndedChanged();
|
||||
}
|
||||
|
||||
|
||||
void ShaderCompilerManager::setIsUnitTesting(bool isUnitTesting)
|
||||
{
|
||||
m_isUnitTesting = isUnitTesting;
|
||||
}
|
||||
|
||||
int ShaderCompilerManager::numberOfJobsStarted()
|
||||
{
|
||||
return m_numberOfJobsStarted;
|
||||
}
|
||||
|
||||
int ShaderCompilerManager::numberOfJobsEnded()
|
||||
{
|
||||
return m_numberOfJobsEnded;
|
||||
}
|
||||
|
||||
int ShaderCompilerManager::numberOfErrors()
|
||||
{
|
||||
return m_numberOfErrors;
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef SHADERCOMPILERMANAGER_H
|
||||
#define SHADERCOMPILERMANAGER_H
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#endif
|
||||
|
||||
typedef QHash<unsigned int, unsigned int> ShaderCompilerJobMap;
|
||||
|
||||
/**
|
||||
* The Shader Compiler Manager class receive a shader compile request
|
||||
* and starts a shader compiler job for it
|
||||
*/
|
||||
class ShaderCompilerManager
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numberOfJobsStarted READ numberOfJobsStarted NOTIFY numberOfJobsStartedChanged)
|
||||
Q_PROPERTY(int numberOfJobsEnded READ numberOfJobsEnded NOTIFY numberOfJobsEndedChanged)
|
||||
Q_PROPERTY(int numberOfErrors READ numberOfErrors NOTIFY numberOfErrorsChanged)
|
||||
public:
|
||||
|
||||
explicit ShaderCompilerManager(QObject* parent = 0);
|
||||
virtual ~ShaderCompilerManager();
|
||||
|
||||
void process(unsigned int connID, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
void decodeShaderCompilerRequest(unsigned int connID, QByteArray payload);
|
||||
void setIsUnitTesting(bool isUnitTesting);
|
||||
int numberOfJobsStarted();
|
||||
int numberOfJobsEnded();
|
||||
int numberOfErrors();
|
||||
virtual void sendResponse(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
|
||||
signals:
|
||||
void sendErrorMessage(QString errorMessage);
|
||||
void sendErrorMessageFromShaderJob(QString errorMessage, QString server, QString timestamp, QString payload);
|
||||
void numberOfJobsStartedChanged();
|
||||
void numberOfJobsEndedChanged();
|
||||
void numberOfErrorsChanged();
|
||||
|
||||
|
||||
public slots:
|
||||
void OnShaderCompilerJobComplete(QByteArray payload, unsigned int requestId);
|
||||
void shaderCompilerError(QString errorMessage, QString server, QString timestamp, QString payload);
|
||||
void jobStarted();
|
||||
void jobEnded();
|
||||
|
||||
|
||||
private:
|
||||
ShaderCompilerJobMap m_shaderCompilerJobMap;
|
||||
bool m_isUnitTesting;
|
||||
int m_numberOfJobsStarted;
|
||||
int m_numberOfJobsEnded;
|
||||
int m_numberOfErrors;
|
||||
};
|
||||
|
||||
#endif // SHADERCOMPILERMANAGER_H
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef SHADERCOMPILERMESSAGES_H
|
||||
#define SHADERCOMPILERMESSAGES_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
struct ShaderCompilerRequestMessage
|
||||
{
|
||||
QByteArray originalPayload;
|
||||
QString serverList;
|
||||
unsigned short serverPort;
|
||||
unsigned int serverListSize;
|
||||
unsigned int requestId;
|
||||
};
|
||||
|
||||
#endif //SHADERCOMPILERMESSAGES_H
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* 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 "shadercompilerModel.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
ShaderCompilerModel* s_singleton = nullptr;
|
||||
}
|
||||
|
||||
ShaderCompilerModel::ShaderCompilerModel(QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
Q_ASSERT(s_singleton == nullptr);
|
||||
s_singleton = this;
|
||||
}
|
||||
|
||||
ShaderCompilerModel::~ShaderCompilerModel()
|
||||
{
|
||||
s_singleton = nullptr;
|
||||
}
|
||||
|
||||
ShaderCompilerModel* ShaderCompilerModel::Get()
|
||||
{
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
QVariant ShaderCompilerModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int row = index.row();
|
||||
|
||||
if (row < 0)
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
if (row >= m_shaderErrorInfoList.count())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case TimeStampRole:
|
||||
return m_shaderErrorInfoList[row].m_shaderTimestamp;
|
||||
case ServerRole:
|
||||
return m_shaderErrorInfoList[row].m_shaderServerName;
|
||||
case ErrorRole:
|
||||
return m_shaderErrorInfoList[row].m_shaderError;
|
||||
case OriginalRequestRole:
|
||||
return m_shaderErrorInfoList[row].m_shaderOriginalPayload;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column())
|
||||
{
|
||||
case ColumnTimeStamp:
|
||||
return m_shaderErrorInfoList[row].m_shaderTimestamp;
|
||||
case ColumnServer:
|
||||
return m_shaderErrorInfoList[row].m_shaderServerName;
|
||||
case ColumnError:
|
||||
return m_shaderErrorInfoList[row].m_shaderServerName;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
Qt::ItemFlags ShaderCompilerModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
(void)index;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
|
||||
int ShaderCompilerModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
(void)parent;
|
||||
return m_shaderErrorInfoList.count();
|
||||
}
|
||||
|
||||
|
||||
QModelIndex ShaderCompilerModel::parent(const QModelIndex&) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
|
||||
QModelIndex ShaderCompilerModel::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
if (row >= rowCount(parent) || column >= columnCount(parent))
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
return createIndex(row, column);
|
||||
}
|
||||
|
||||
|
||||
int ShaderCompilerModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : Column::Max;
|
||||
}
|
||||
|
||||
|
||||
QVariant ShaderCompilerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case ColumnTimeStamp:
|
||||
return tr("Time Stamp");
|
||||
case ColumnServer:
|
||||
return tr("Server");
|
||||
case ColumnError:
|
||||
return tr("Error");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QAbstractItemModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
|
||||
QHash<int, QByteArray> ShaderCompilerModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result;
|
||||
result[TimeStampRole] = "timestamp";
|
||||
result[ServerRole] = "server";
|
||||
result[ErrorRole] = "error";
|
||||
result[OriginalRequestRole] = "originalRequest";
|
||||
return result;
|
||||
}
|
||||
void ShaderCompilerModel::addShaderErrorInfoEntry(QString errorMessage, QString timestamp, QString payload, QString server)
|
||||
{
|
||||
ShaderCompilerErrorInfo shaderCompileErrorInfo(errorMessage, timestamp, payload, server);
|
||||
beginInsertRows(QModelIndex(), m_shaderErrorInfoList.size(), m_shaderErrorInfoList.size());
|
||||
m_shaderErrorInfoList.append(shaderCompileErrorInfo);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef SHADERCOMPILERMODEL_H
|
||||
#define SHADERCOMPILERMODEL_H
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QAbstractItemModel>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
class QModelIndex;
|
||||
class QObject;
|
||||
|
||||
struct ShaderCompilerErrorInfo
|
||||
{
|
||||
QString m_shaderError;
|
||||
QString m_shaderTimestamp;
|
||||
QString m_shaderOriginalPayload;
|
||||
QString m_shaderServerName;
|
||||
|
||||
ShaderCompilerErrorInfo(QString shaderError, QString shaderTimestamp, QString shaderOriginalPayload, QString shaderServerName)
|
||||
: m_shaderError(shaderError)
|
||||
, m_shaderTimestamp(shaderTimestamp)
|
||||
, m_shaderOriginalPayload(shaderOriginalPayload)
|
||||
, m_shaderServerName(shaderServerName)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** The Shader Compiler model is responsible for capturing error requests
|
||||
*/
|
||||
class ShaderCompilerModel
|
||||
: public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
enum DataRoles
|
||||
{
|
||||
TimeStampRole = Qt::UserRole + 1,
|
||||
ServerRole,
|
||||
ErrorRole,
|
||||
OriginalRequestRole,
|
||||
};
|
||||
|
||||
enum Column
|
||||
{
|
||||
ColumnTimeStamp,
|
||||
ColumnServer,
|
||||
ColumnError,
|
||||
Max
|
||||
};
|
||||
|
||||
/// standard Qt constructor
|
||||
explicit ShaderCompilerModel(QObject* parent = 0);
|
||||
virtual ~ShaderCompilerModel();
|
||||
|
||||
// singleton pattern
|
||||
static ShaderCompilerModel* Get();
|
||||
|
||||
|
||||
/// QAbstractListModel interface
|
||||
QModelIndex parent(const QModelIndex&) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex&) const override;
|
||||
virtual int rowCount(const QModelIndex& parent) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
virtual QVariant data(const QModelIndex& index, int role) const override;
|
||||
virtual QHash<int, QByteArray> roleNames() const override;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
public slots:
|
||||
void addShaderErrorInfoEntry(QString errorMessage, QString timestamp, QString payload, QString server);
|
||||
|
||||
private:
|
||||
|
||||
QList<ShaderCompilerErrorInfo> m_shaderErrorInfoList;
|
||||
};
|
||||
|
||||
|
||||
#endif // SHADERCOMPILERMODEL_H
|
||||
|
||||
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* 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 "shadercompilerjob.h"
|
||||
#include "native/assetprocessor.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
ShaderCompilerJob::ShaderCompilerJob()
|
||||
: m_isUnitTesting(false)
|
||||
, m_manager(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderCompilerJob::~ShaderCompilerJob()
|
||||
{
|
||||
m_manager = nullptr;
|
||||
}
|
||||
|
||||
ShaderCompilerRequestMessage ShaderCompilerJob::ShaderCompilerMessage() const
|
||||
{
|
||||
return m_ShaderCompilerMessage;
|
||||
}
|
||||
|
||||
void ShaderCompilerJob::initialize(QObject* pManager, const ShaderCompilerRequestMessage& ShaderCompilerMessage)
|
||||
{
|
||||
m_manager = pManager;
|
||||
m_ShaderCompilerMessage = ShaderCompilerMessage;
|
||||
}
|
||||
|
||||
QString ShaderCompilerJob::getServerAddress()
|
||||
{
|
||||
if (isServerListEmpty())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString serverAddress;
|
||||
if (!m_ShaderCompilerMessage.serverList.contains(","))
|
||||
{
|
||||
serverAddress = m_ShaderCompilerMessage.serverList;
|
||||
m_ShaderCompilerMessage.serverList.clear();
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
QStringList serverList = m_ShaderCompilerMessage.serverList.split(",");
|
||||
serverAddress = serverList.takeAt(0);
|
||||
m_ShaderCompilerMessage.serverList = serverList.join(",");
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
bool ShaderCompilerJob::isServerListEmpty()
|
||||
{
|
||||
return m_ShaderCompilerMessage.serverList.isEmpty();
|
||||
}
|
||||
|
||||
bool ShaderCompilerJob::attemptDelivery(QString serverAddress, QByteArray& payload)
|
||||
{
|
||||
QTcpSocket socket;
|
||||
QString error;
|
||||
int waitingTime = 8000; // 8 sec timeout for sending.
|
||||
int jobCompileMaxTime = 1000 * 60; // 60 sec timeout for compilation
|
||||
if (m_isUnitTesting)
|
||||
{
|
||||
waitingTime = 500;
|
||||
jobCompileMaxTime = 500;
|
||||
}
|
||||
|
||||
socket.connectToHost(serverAddress, m_ShaderCompilerMessage.serverPort, QIODevice::ReadWrite);
|
||||
|
||||
if (socket.waitForConnected(waitingTime))
|
||||
{
|
||||
qint64 bytesWritten = 0;
|
||||
qint64 payloadSize = static_cast<qint64>(m_ShaderCompilerMessage.originalPayload.size());
|
||||
// send payload size to server
|
||||
while (bytesWritten != sizeof(qint64))
|
||||
{
|
||||
qint64 currentWrite = socket.write(reinterpret_cast<char*>(&payloadSize) + bytesWritten,
|
||||
sizeof(qint64) - bytesWritten);
|
||||
if (currentWrite == -1)
|
||||
{
|
||||
//It is important to note that we are only outputting the error to debugchannel only here because
|
||||
//we are forwarding these error messages upstream to the manager,who will take the appropriate action
|
||||
error = "Connection Lost:Unable to send data";
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
|
||||
QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
|
||||
return false;
|
||||
}
|
||||
socket.flush();
|
||||
bytesWritten += currentWrite;
|
||||
}
|
||||
bytesWritten = 0;
|
||||
//send actual payload to server
|
||||
while (bytesWritten != m_ShaderCompilerMessage.originalPayload.size())
|
||||
{
|
||||
qint64 currentWrite = socket.write(m_ShaderCompilerMessage.originalPayload.data() + bytesWritten,
|
||||
m_ShaderCompilerMessage.originalPayload.size() - bytesWritten);
|
||||
if (currentWrite == -1)
|
||||
{
|
||||
error = "Connection Lost:Unable to send data";
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
|
||||
QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
|
||||
}
|
||||
socket.flush();
|
||||
bytesWritten += currentWrite;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "Unable to connect to IP Address " + serverAddress;
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
|
||||
QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int expectedBytes = sizeof(unsigned int) + sizeof(qint8);
|
||||
unsigned int bytesReadTotal = 0;
|
||||
unsigned int messageSize = 0;
|
||||
bool isMessageSizeKnown = false;
|
||||
//read the entire payload
|
||||
while ((bytesReadTotal < expectedBytes + messageSize))
|
||||
{
|
||||
if (socket.bytesAvailable() == 0)
|
||||
{
|
||||
if (!socket.waitForReadyRead(jobCompileMaxTime))
|
||||
{
|
||||
error = "Remote IP is taking too long to respond: " + serverAddress;
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
|
||||
QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
|
||||
payload.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 bytesAvailable = socket.bytesAvailable();
|
||||
|
||||
if (bytesAvailable >= expectedBytes && !isMessageSizeKnown)
|
||||
{
|
||||
socket.peek(reinterpret_cast<char*>(&messageSize), sizeof(unsigned int));
|
||||
payload.resize(expectedBytes + messageSize);
|
||||
isMessageSizeKnown = true;
|
||||
}
|
||||
|
||||
if (bytesAvailable > 0)
|
||||
{
|
||||
qint64 bytesRead = socket.read(payload.data() + bytesReadTotal, bytesAvailable);
|
||||
|
||||
if (bytesRead <= 0)
|
||||
{
|
||||
error = "Connection closed by remote IP Address " + serverAddress;
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
|
||||
QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
|
||||
payload.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesReadTotal = aznumeric_cast<uint32_t>(bytesReadTotal + bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
return true; // payload successfully send
|
||||
}
|
||||
|
||||
void ShaderCompilerJob::run()
|
||||
{
|
||||
QMetaObject::invokeMethod(m_manager, "jobStarted", Qt::QueuedConnection);
|
||||
QByteArray payload;
|
||||
//until server list is empty, keep trying
|
||||
while (!isServerListEmpty())
|
||||
{
|
||||
QString serverAddress = getServerAddress();
|
||||
//attempt to send payload
|
||||
if (attemptDelivery(serverAddress, payload))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
//we are appending request id at the end of every payload,
|
||||
//therefore in the case of any errors also
|
||||
//we will be sending atleast four bytes to the game
|
||||
payload.append(reinterpret_cast<char*>(&m_ShaderCompilerMessage.requestId), sizeof(unsigned int));
|
||||
QMetaObject::invokeMethod(m_manager, "OnShaderCompilerJobComplete", Qt::QueuedConnection, Q_ARG(QByteArray, payload), Q_ARG(unsigned int, m_ShaderCompilerMessage.requestId));
|
||||
QMetaObject::invokeMethod(m_manager, "jobEnded", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
void ShaderCompilerJob::setIsUnitTesting(bool isUnitTesting)
|
||||
{
|
||||
m_isUnitTesting = isUnitTesting;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef SHADERCOMPILERJOB_H
|
||||
#define SHADERCOMPILERJOB_H
|
||||
|
||||
#include <QRunnable>
|
||||
#include "shadercompilerMessages.h"
|
||||
|
||||
class QByteArray;
|
||||
class QObject;
|
||||
|
||||
/**
|
||||
* This class is responsible for connecting to the shader compiler server
|
||||
* and getting back the response to the shader compiler manager
|
||||
*/
|
||||
class ShaderCompilerJob
|
||||
: public QRunnable
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ShaderCompilerJob();
|
||||
virtual ~ShaderCompilerJob();
|
||||
ShaderCompilerRequestMessage ShaderCompilerMessage() const;
|
||||
void initialize(QObject* pManager, const ShaderCompilerRequestMessage& ShaderCompilerMessage);
|
||||
QString getServerAddress();
|
||||
bool isServerListEmpty();
|
||||
virtual void run() override;
|
||||
|
||||
void setIsUnitTesting(bool isUnitTesting);
|
||||
|
||||
bool attemptDelivery(QString serverAddress, QByteArray& payload);
|
||||
|
||||
private:
|
||||
ShaderCompilerRequestMessage m_ShaderCompilerMessage;
|
||||
QObject* m_manager;
|
||||
bool m_isUnitTesting;
|
||||
};
|
||||
|
||||
#endif // SHADERCOMPILERJOB_H
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
AZ_UNIT_TEST_HOOK(new BaseAssetProcessorTestEnvironment)
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
class UnitTestAppManager : public BatchApplicationManager
|
||||
@@ -35,7 +33,7 @@ namespace AssetProcessor
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// tests which use the builder bus plug in their own mock version, so disconnect ours.
|
||||
AssetProcessor::AssetBuilderInfoBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -59,7 +57,7 @@ namespace AssetProcessor
|
||||
void SetUp() override
|
||||
{
|
||||
AssetProcessorTest::SetUp();
|
||||
|
||||
|
||||
static int numParams = 1;
|
||||
static char processName[] = {"AssetProcessorBatch"};
|
||||
static char* namePtr = &processName[0];
|
||||
@@ -147,7 +145,7 @@ namespace AssetProcessor
|
||||
time.start();
|
||||
|
||||
actualTest->StartTest();
|
||||
|
||||
|
||||
while (!testIsComplete)
|
||||
{
|
||||
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
|
||||
|
||||
@@ -5,76 +5,8 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include "utilities/BatchApplicationManager.h"
|
||||
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzTest/Utils.h>
|
||||
#include <native/tests/BaseAssetProcessorTest.h>
|
||||
|
||||
DECLARE_AZ_UNIT_TEST_MAIN()
|
||||
|
||||
int RunUnitTests(int argc, char* argv[], bool& ranUnitTests)
|
||||
{
|
||||
ranUnitTests = true;
|
||||
|
||||
INVOKE_AZ_UNIT_TEST_MAIN(nullptr); // nullptr turns off default test environment used to catch stray asserts
|
||||
|
||||
// This looks a bit weird, but the macro returns conditionally, so *if* we get here, it means the unit tests didn't run
|
||||
ranUnitTests = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "1");
|
||||
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
|
||||
// If "--unittest" is present on the command line, run unit testing
|
||||
// and return immediately. Otherwise, continue as normal.
|
||||
AZ::Test::addTestEnvironment(new BaseAssetProcessorTestEnvironment());
|
||||
|
||||
bool pauseOnComplete = false;
|
||||
|
||||
if (AZ::Test::ContainsParameter(argc, argv, "--pause-on-completion"))
|
||||
{
|
||||
pauseOnComplete = true;
|
||||
}
|
||||
|
||||
bool ranUnitTests;
|
||||
int result = RunUnitTests(argc, argv, ranUnitTests);
|
||||
|
||||
if (ranUnitTests)
|
||||
{
|
||||
if (pauseOnComplete)
|
||||
{
|
||||
system("pause");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BatchApplicationManager applicationManager(&argc, &argv);
|
||||
setvbuf(stdout, NULL, _IONBF, 0); // Disabling output buffering to fix test failures due to incomplete logs
|
||||
|
||||
ApplicationManager::BeforeRunStatus status = applicationManager.BeforeRun();
|
||||
|
||||
if (status != ApplicationManager::BeforeRunStatus::Status_Success)
|
||||
{
|
||||
if (status == ApplicationManager::BeforeRunStatus::Status_Restarting)
|
||||
{
|
||||
//AssetProcessor will restart
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Initialization failed
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return applicationManager.Run() ? 0 : 1;
|
||||
}
|
||||
|
||||
AZ_UNIT_TEST_HOOK(new BaseAssetProcessorTestEnvironment)
|
||||
|
||||
@@ -29,14 +29,16 @@ namespace AssetProcessor
|
||||
AssetTreeItem::AssetTreeItem(
|
||||
AZStd::shared_ptr<AssetTreeItemData> data,
|
||||
QIcon errorIcon,
|
||||
QIcon folderIcon,
|
||||
QIcon fileIcon,
|
||||
AssetTreeItem* parentItem) :
|
||||
m_data(data),
|
||||
m_parent(parentItem),
|
||||
m_errorIcon(errorIcon), // QIcon is implicitily shared.
|
||||
m_folderIcon(QIcon(QStringLiteral(":/Gallery/Asset_Folder.svg"))),
|
||||
m_fileIcon(QIcon(QStringLiteral(":/Gallery/Asset_File.svg")))
|
||||
m_errorIcon(errorIcon), // QIcon is implicitly shared.
|
||||
m_folderIcon(folderIcon),
|
||||
m_fileIcon(fileIcon)
|
||||
{
|
||||
m_folderIcon.addFile(QStringLiteral(":/Gallery/Asset_Folder.svg"), QSize(), QIcon::Selected);
|
||||
|
||||
}
|
||||
|
||||
AssetTreeItem::~AssetTreeItem()
|
||||
@@ -45,7 +47,7 @@ namespace AssetProcessor
|
||||
|
||||
AssetTreeItem* AssetTreeItem::CreateChild(AZStd::shared_ptr<AssetTreeItemData> data)
|
||||
{
|
||||
m_childItems.emplace_back(new AssetTreeItem(data, m_errorIcon, this));
|
||||
m_childItems.emplace_back(new AssetTreeItem(data, m_errorIcon, m_folderIcon, m_fileIcon, this));
|
||||
return m_childItems.back().get();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace AssetProcessor
|
||||
explicit AssetTreeItem(
|
||||
AZStd::shared_ptr<AssetTreeItemData> data,
|
||||
QIcon errorIcon,
|
||||
QIcon folderIcon,
|
||||
QIcon fileIcon,
|
||||
AssetTreeItem* parentItem = nullptr);
|
||||
virtual ~AssetTreeItem();
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@ namespace AssetProcessor
|
||||
|
||||
AssetTreeModel::AssetTreeModel(AZStd::shared_ptr<AzToolsFramework::AssetDatabase::AssetDatabaseConnection> sharedDbConnection, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
m_sharedDbConnection(sharedDbConnection),
|
||||
m_errorIcon(QStringLiteral(":/stylesheet/img/logging/error.svg"))
|
||||
m_sharedDbConnection(sharedDbConnection)
|
||||
, m_errorIcon(QStringLiteral(":/stylesheet/img/logging/error.svg"))
|
||||
, m_folderIcon(QIcon(QStringLiteral(":/Gallery/Asset_Folder.svg")))
|
||||
, m_fileIcon(QIcon(QStringLiteral(":/Gallery/Asset_File.svg")))
|
||||
{
|
||||
m_folderIcon.addFile(QStringLiteral(":/Gallery/Asset_Folder.svg"), QSize(), QIcon::Selected);
|
||||
ApplicationManagerNotifications::Bus::Handler::BusConnect();
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
@@ -40,7 +43,7 @@ namespace AssetProcessor
|
||||
void AssetTreeModel::Reset()
|
||||
{
|
||||
beginResetModel();
|
||||
m_root.reset(new AssetTreeItem(AZStd::make_shared<AssetTreeItemData>("", "", true, AZ::Uuid::CreateNull()), m_errorIcon));
|
||||
m_root.reset(new AssetTreeItem(AZStd::make_shared<AssetTreeItemData>("", "", true, AZ::Uuid::CreateNull()), m_errorIcon, m_folderIcon, m_fileIcon));
|
||||
|
||||
ResetModel();
|
||||
|
||||
|
||||
@@ -54,5 +54,7 @@ namespace AssetProcessor
|
||||
AZStd::shared_ptr<AzToolsFramework::AssetDatabase::AssetDatabaseConnection> m_sharedDbConnection;
|
||||
|
||||
QIcon m_errorIcon;
|
||||
QIcon m_folderIcon;
|
||||
QIcon m_fileIcon;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "../connection/connection.h"
|
||||
#include "../resourcecompiler/rccontroller.h"
|
||||
#include "../resourcecompiler/RCJobSortFilterProxyModel.h"
|
||||
#include "../shadercompiler/shadercompilerModel.h"
|
||||
|
||||
|
||||
#include <QClipboard>
|
||||
@@ -148,7 +147,6 @@ void MainWindow::Activate()
|
||||
ui->buttonList->addTab(QStringLiteral("Jobs"));
|
||||
ui->buttonList->addTab(QStringLiteral("Assets"));
|
||||
ui->buttonList->addTab(QStringLiteral("Logs"));
|
||||
ui->buttonList->addTab(QStringLiteral("Shaders"));
|
||||
ui->buttonList->addTab(QStringLiteral("Connections"));
|
||||
ui->buttonList->addTab(QStringLiteral("Tools"));
|
||||
|
||||
@@ -167,7 +165,7 @@ void MainWindow::Activate()
|
||||
ui->connectionTreeView->header()->resizeSection(ConnectionManager::PortColumn, 60);
|
||||
ui->connectionTreeView->header()->resizeSection(ConnectionManager::PlatformColumn, 60);
|
||||
ui->connectionTreeView->header()->resizeSection(ConnectionManager::AutoConnectColumn, 60);
|
||||
|
||||
|
||||
ui->connectionTreeView->header()->setStretchLastSection(false);
|
||||
connect(ui->connectionTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::OnConnectionSelectionChanged);
|
||||
|
||||
@@ -191,12 +189,12 @@ void MainWindow::Activate()
|
||||
ui->allowListAllowedListConnectionsListView->setModel(&m_allowedListAddresses);
|
||||
connect(ui->allowedListRejectedConnectionsListView, &QListView::clicked, this, &MainWindow::OnRejectedConnectionsListViewClicked);
|
||||
ui->allowedListRejectedConnectionsListView->setModel(&m_rejectedAddresses);
|
||||
|
||||
|
||||
connect(ui->allowedListEnableCheckBox, &QCheckBox::toggled, this, &MainWindow::OnAllowedListCheckBoxToggled);
|
||||
|
||||
|
||||
connect(ui->allowedListAddHostNameToolButton, &QToolButton::clicked, this, &MainWindow::OnAddHostNameAllowedListButtonClicked);
|
||||
connect(ui->allowedListAddIPToolButton, &QPushButton::clicked, this, &MainWindow::OnAddIPAllowedListButtonClicked);
|
||||
|
||||
|
||||
connect(ui->allowedListToAllowedListToolButton, &QPushButton::clicked, this, &MainWindow::OnToAllowedListButtonClicked);
|
||||
connect(ui->allowedListToRejectedListToolButton, &QToolButton::clicked, this, &MainWindow::OnToRejectedListButtonClicked);
|
||||
|
||||
@@ -206,7 +204,7 @@ void MainWindow::Activate()
|
||||
|
||||
QRegExpValidator* hostNameValidator = new QRegExpValidator(validHostName, this);
|
||||
ui->allowedListAddHostNameLineEdit->setValidator(hostNameValidator);
|
||||
|
||||
|
||||
QRegExpValidator* ipValidator = new QRegExpValidator(validIP, this);
|
||||
ui->allowedListAddIPLineEdit->setValidator(ipValidator);
|
||||
|
||||
@@ -237,7 +235,7 @@ void MainWindow::Activate()
|
||||
m_logSortFilterProxy->setSourceModel(m_logsModel);
|
||||
m_logSortFilterProxy->setFilterKeyColumn(AzToolsFramework::Logging::LogTableModel::ColumnMessage);
|
||||
m_logSortFilterProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
|
||||
ui->jobLogTableView->setModel(m_logSortFilterProxy);
|
||||
ui->jobLogTableView->setItemDelegate(new AzToolsFramework::Logging::LogTableItemDelegate(ui->jobLogTableView));
|
||||
ui->jobLogTableView->setExpandOnSelection();
|
||||
@@ -317,14 +315,6 @@ void MainWindow::Activate()
|
||||
connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
|
||||
this, writeJobFilterSettings);
|
||||
|
||||
//Shader view
|
||||
ui->shaderTreeView->setModel(m_guiApplicationManager->GetShaderCompilerModel());
|
||||
ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnTimeStamp, 80);
|
||||
ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnServer, 40);
|
||||
ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnError, 220);
|
||||
ui->shaderTreeView->header()->setSectionResizeMode(ShaderCompilerModel::ColumnError, QHeaderView::Stretch);
|
||||
ui->shaderTreeView->header()->setStretchLastSection(false);
|
||||
|
||||
// Asset view
|
||||
m_sourceAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
|
||||
m_sourceModel = new AssetProcessor::SourceAssetTreeModel(m_sharedDbConnection, this);
|
||||
@@ -410,7 +400,7 @@ void MainWindow::Activate()
|
||||
bool zeroAnalysisModeFromSettings = settings.value("EnableZeroAnalysis", QVariant(true)).toBool();
|
||||
settings.endGroup();
|
||||
|
||||
QObject::connect(ui->modtimeSkippingCheckBox, &QCheckBox::stateChanged, this,
|
||||
QObject::connect(ui->modtimeSkippingCheckBox, &QCheckBox::stateChanged, this,
|
||||
[this](int newCheckState)
|
||||
{
|
||||
bool newOption = newCheckState == Qt::Checked ? true : false;
|
||||
@@ -553,7 +543,7 @@ void MainWindow::OnAddConnection(bool /*checked*/)
|
||||
m_guiApplicationManager->GetConnectionManager()->addUserConnection();
|
||||
}
|
||||
|
||||
void MainWindow::OnAllowedListConnectionsListViewClicked()
|
||||
void MainWindow::OnAllowedListConnectionsListViewClicked()
|
||||
{
|
||||
ui->allowedListRejectedConnectionsListView->clearSelection();
|
||||
}
|
||||
@@ -563,7 +553,7 @@ void MainWindow::OnRejectedConnectionsListViewClicked()
|
||||
ui->allowListAllowedListConnectionsListView->clearSelection();
|
||||
}
|
||||
|
||||
void MainWindow::OnAllowedListCheckBoxToggled()
|
||||
void MainWindow::OnAllowedListCheckBoxToggled()
|
||||
{
|
||||
if (!ui->allowedListEnableCheckBox->isChecked())
|
||||
{
|
||||
@@ -598,7 +588,7 @@ void MainWindow::OnAllowedListCheckBoxToggled()
|
||||
ui->allowedListToAllowedListToolButton->setEnabled(true);
|
||||
ui->allowedListToRejectedListToolButton->setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
m_guiApplicationManager->GetConnectionManager()->AllowedListingEnabled(ui->allowedListEnableCheckBox->isChecked());
|
||||
}
|
||||
|
||||
@@ -868,7 +858,7 @@ void MainWindow::OnAssetProcessorStatusChanged(const AssetProcessor::AssetProces
|
||||
text = tr("Working, analyzing jobs remaining %1, processing jobs remaining %2...").arg(m_createJobCount).arg(m_processJobsCount);
|
||||
ui->timerContainerWidget->setVisible(false);
|
||||
ui->productAssetDetailsPanel->SetScanQueueEnabled(false);
|
||||
|
||||
|
||||
IntervalAssetTabFilterRefresh();
|
||||
}
|
||||
else
|
||||
@@ -887,7 +877,7 @@ void MainWindow::OnAssetProcessorStatusChanged(const AssetProcessor::AssetProces
|
||||
break;
|
||||
case AssetProcessorStatus::Processing_Jobs:
|
||||
CheckStartProcessTimers();
|
||||
m_processJobsCount = entry.m_count;
|
||||
m_processJobsCount = entry.m_count;
|
||||
|
||||
if (m_processJobsCount + m_createJobCount > 0)
|
||||
{
|
||||
@@ -993,7 +983,7 @@ void MainWindow::ApplyConfig()
|
||||
ui->jobLogTableView->header()->resizeSection(AzToolsFramework::Logging::LogTableModel::ColumnType, m_config.logTypeColumnWidth);
|
||||
}
|
||||
|
||||
MainWindow::LogSortFilterProxy::LogSortFilterProxy(QObject* parentOjbect) : QSortFilterProxyModel(parentOjbect)
|
||||
MainWindow::LogSortFilterProxy::LogSortFilterProxy(QObject* parentOjbect) : QSortFilterProxyModel(parentOjbect)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1312,7 +1302,7 @@ void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
|
||||
ui->sourceAssetDetailsPanel->GoToSource(item->m_elementId.GetInputAssetName().toUtf8().constData());
|
||||
});
|
||||
|
||||
QString productMenuTitle(tr("View product asset..."));
|
||||
QString productMenuTitle(tr("View product asset..."));
|
||||
if (item->m_jobState != AzToolsFramework::AssetSystem::JobStatus::Completed)
|
||||
{
|
||||
QString disabledActionTooltip(tr("Only completed jobs are available in the Assets tab."));
|
||||
@@ -1620,7 +1610,7 @@ void MainWindow::ShowProductAssetContextMenu(const QPoint& pos)
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(pathToProduct.GetValue());
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
QString fileOrFolder(cachedAsset->getChildCount() > 0 ? tr("folder") : tr("file"));
|
||||
|
||||
@@ -65,7 +65,6 @@ public:
|
||||
Jobs,
|
||||
Assets,
|
||||
Logs,
|
||||
Shaders,
|
||||
Connections,
|
||||
Tools
|
||||
};
|
||||
|
||||
@@ -763,59 +763,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="shaderDialog">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="shaderLabel">
|
||||
<property name="text">
|
||||
<string>Shaders</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="shaderInfoPane" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="AzQtComponents::TableView" name="shaderTreeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="connectionsDialog">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="spacing">
|
||||
|
||||
@@ -24,14 +24,12 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
|
||||
FileWatcher fileWatcher;
|
||||
|
||||
FolderWatchCallbackEx folderWatch(tempPath, "", true);
|
||||
|
||||
fileWatcher.AddFolderWatch(&folderWatch);
|
||||
fileWatcher.AddFolderWatch(tempPath);
|
||||
fileWatcher.StartWatching();
|
||||
|
||||
{ // test a single file create/write
|
||||
bool foundFile = false;
|
||||
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
|
||||
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Single file test Found asset: %s.\n", filename.toUtf8().data());
|
||||
foundFile = true;
|
||||
@@ -66,7 +64,7 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
const unsigned long maxFiles = 10000;
|
||||
QSet<QString> outstandingFiles;
|
||||
|
||||
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
|
||||
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
|
||||
{
|
||||
outstandingFiles.remove(filename);
|
||||
});
|
||||
@@ -122,7 +120,7 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
|
||||
{ // test deletion
|
||||
bool foundFile = false;
|
||||
auto connection = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileRemoved, this, [&](QString filename)
|
||||
auto connection = QObject::connect(&fileWatcher, &FileWatcher::fileRemoved, this, [&](QString filename)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Deleted asset: %s...\n", filename.toUtf8().data());
|
||||
foundFile = true;
|
||||
@@ -155,7 +153,7 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
{
|
||||
bool fileAddCalled = false;
|
||||
QString fileAddName;
|
||||
auto connectionAdd = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileAdded, this, [&](QString filename)
|
||||
auto connectionAdd = QObject::connect(&fileWatcher, &FileWatcher::fileAdded, this, [&](QString filename)
|
||||
{
|
||||
fileAddCalled = true;
|
||||
fileAddName = filename;
|
||||
@@ -163,7 +161,7 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
|
||||
bool fileRemoveCalled = false;
|
||||
QString fileRemoveName;
|
||||
auto connectionRemove = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileRemoved, this, [&](QString filename)
|
||||
auto connectionRemove = QObject::connect(&fileWatcher, &FileWatcher::fileRemoved, this, [&](QString filename)
|
||||
{
|
||||
fileRemoveCalled = true;
|
||||
fileRemoveName = filename;
|
||||
@@ -171,7 +169,7 @@ void FileWatcherUnitTestRunner::StartTest()
|
||||
|
||||
QStringList fileModifiedNames;
|
||||
bool fileModifiedCalled = false;
|
||||
auto connectionModified = QObject::connect(&folderWatch, &FolderWatchCallbackEx::fileModified, this, [&](QString filename)
|
||||
auto connectionModified = QObject::connect(&fileWatcher, &FileWatcher::fileModified, this, [&](QString filename)
|
||||
{
|
||||
fileModifiedCalled = true;
|
||||
fileModifiedNames.append(filename);
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* 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 "ShaderCompilerUnitTests.h"
|
||||
#include "native/connection/connectionManager.h"
|
||||
#include "native/connection/connection.h"
|
||||
#include "native/utilities/assetUtils.h"
|
||||
|
||||
#define UNIT_TEST_CONNECT_PORT 12125
|
||||
|
||||
ShaderCompilerUnitTest::ShaderCompilerUnitTest()
|
||||
{
|
||||
m_connectionManager = ConnectionManager::Get();
|
||||
connect(this, SIGNAL(StartUnitTestForGoodShaderCompiler()), this, SLOT(UnitTestForGoodShaderCompiler()));
|
||||
connect(this, SIGNAL(StartUnitTestForFirstBadShaderCompiler()), this, SLOT(UnitTestForFirstBadShaderCompiler()));
|
||||
connect(this, SIGNAL(StartUnitTestForSecondBadShaderCompiler()), this, SLOT(UnitTestForSecondBadShaderCompiler()));
|
||||
connect(this, SIGNAL(StartUnitTestForThirdBadShaderCompiler()), this, SLOT(UnitTestForThirdBadShaderCompiler()));
|
||||
connect(&m_shaderCompilerManager, SIGNAL(sendErrorMessageFromShaderJob(QString, QString, QString, QString)), this, SLOT(ReceiveShaderCompilerErrorMessage(QString, QString, QString, QString)));
|
||||
|
||||
m_shaderCompilerManager.setIsUnitTesting(true);
|
||||
m_connectionManager->RegisterService(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), AZStd::bind(&ShaderCompilerManager::process, &m_shaderCompilerManager, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4));
|
||||
ContructPayloadForShaderCompilerServer(m_testPayload);
|
||||
}
|
||||
|
||||
ShaderCompilerUnitTest::~ShaderCompilerUnitTest()
|
||||
{
|
||||
m_connectionManager->removeConnection(m_connectionId);
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::ContructPayloadForShaderCompilerServer(QByteArray& payload)
|
||||
{
|
||||
QString testString = "This is a test string";
|
||||
QString testServerList = "127.0.0.3,198.51.100.0,127.0.0.1"; // note - 198.51.100.0 is in the 'test' range that will never be assigned to anyone.
|
||||
unsigned int testServerListLength = static_cast<unsigned int>(testServerList.size());
|
||||
unsigned short testServerPort = 12348;
|
||||
unsigned int testRequestId = 1;
|
||||
qint64 testStringLength = static_cast<qint64>(testString.size());
|
||||
payload.resize(static_cast<unsigned int>(testStringLength));
|
||||
memcpy(payload.data(), (testString.toStdString().c_str()), testStringLength);
|
||||
unsigned int payloadSize = payload.size();
|
||||
payload.resize(payloadSize + 1 + static_cast<unsigned int>(testServerListLength) + 1 + sizeof(unsigned short) + sizeof(unsigned int) + sizeof(unsigned int));
|
||||
char* dataStart = payload.data() + payloadSize;
|
||||
*dataStart = 0;// null
|
||||
memcpy(payload.data() + payloadSize + 1, (testServerList.toStdString().c_str()), testServerListLength);
|
||||
dataStart += 1 + testServerListLength;
|
||||
*dataStart = 0; //null
|
||||
memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1, reinterpret_cast<char*>(&testServerPort), sizeof(unsigned short));
|
||||
memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1 + sizeof(unsigned short), reinterpret_cast<char*>(&testServerListLength), sizeof(unsigned int));
|
||||
memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1 + sizeof(unsigned short) + sizeof(unsigned int), reinterpret_cast<char*>(&testRequestId), sizeof(unsigned int));
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::StartTest()
|
||||
{
|
||||
m_connectionId = m_connectionManager->addConnection();
|
||||
Connection* connection = m_connectionManager->getConnection(m_connectionId);
|
||||
connection->SetPort(UNIT_TEST_CONNECT_PORT);
|
||||
connection->SetIpAddress("127.0.0.1");
|
||||
connection->SetAutoConnect(true);
|
||||
UnitTestForGoodShaderCompiler();
|
||||
}
|
||||
|
||||
int ShaderCompilerUnitTest::UnitTestPriority() const
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::UnitTestForGoodShaderCompiler()
|
||||
{
|
||||
AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'good' shader compiler...\n");
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForGoodShaderCompiler, this , AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
|
||||
m_server.Init("127.0.0.1", 12348);
|
||||
m_server.setServerStatus(UnitTestShaderCompilerServer::GoodServer);
|
||||
m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::UnitTestForFirstBadShaderCompiler()
|
||||
{
|
||||
AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Incomplete Payload)\n");
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForFirstBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
|
||||
m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_SendsIncompletePayload);
|
||||
m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::UnitTestForSecondBadShaderCompiler()
|
||||
{
|
||||
AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Payload followed by disconnection)\n");
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForSecondBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
|
||||
m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_ReadsPayloadAndDisconnect);
|
||||
m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::UnitTestForThirdBadShaderCompiler()
|
||||
{
|
||||
AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Connect but disconnect without data)\n");
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForThirdBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
|
||||
m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_DisconnectAfterConnect);
|
||||
m_server.startServer();
|
||||
m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::VerifyPayloadForGoodShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
|
||||
{
|
||||
(void) connId;
|
||||
(void) type;
|
||||
(void) serial;
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
|
||||
|
||||
unsigned int messageSize;
|
||||
quint8 status;
|
||||
QByteArray payloadToCheck;
|
||||
unsigned int requestId;
|
||||
memcpy((&messageSize), payload.data(), sizeof(unsigned int));
|
||||
memcpy((&status), payload.data() + sizeof(unsigned int), sizeof(unsigned char));
|
||||
payloadToCheck.resize(messageSize);
|
||||
memcpy((payloadToCheck.data()), payload.data() + sizeof(unsigned int) + sizeof(unsigned char), messageSize);
|
||||
memcpy((&requestId), payload.data() + sizeof(unsigned int) + sizeof(unsigned char) + messageSize, sizeof(unsigned int));
|
||||
QString outgoingTestString = "Test string validated";
|
||||
if (QString::compare(QString(payloadToCheck), outgoingTestString, Qt::CaseSensitive) != 0)
|
||||
{
|
||||
Q_EMIT UnitTestFailed("Unit Test for Good Shader Compiler Failed");
|
||||
return;
|
||||
}
|
||||
Q_EMIT StartUnitTestForFirstBadShaderCompiler();
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::VerifyPayloadForFirstBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
|
||||
{
|
||||
(void) connId;
|
||||
(void) type;
|
||||
(void) serial;
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
|
||||
QString error = "Remote IP is taking too long to respond: 127.0.0.1";
|
||||
if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
|
||||
{
|
||||
Q_EMIT UnitTestFailed("Unit Test for First Bad Shader Compiler Failed");
|
||||
return;
|
||||
}
|
||||
m_lastShaderCompilerErrorMessage.clear();
|
||||
Q_EMIT StartUnitTestForSecondBadShaderCompiler();
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::VerifyPayloadForSecondBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
|
||||
{
|
||||
(void) connId;
|
||||
(void) type;
|
||||
(void) serial;
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
|
||||
QString error = "Remote IP is taking too long to respond: 127.0.0.1";
|
||||
if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
|
||||
{
|
||||
Q_EMIT UnitTestFailed("Unit Test for Second Bad Shader Compiler Failed");
|
||||
return;
|
||||
}
|
||||
m_lastShaderCompilerErrorMessage.clear();
|
||||
Q_EMIT StartUnitTestForThirdBadShaderCompiler();
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::VerifyPayloadForThirdBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
|
||||
{
|
||||
(void) connId;
|
||||
(void) type;
|
||||
(void) serial;
|
||||
m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
|
||||
QString error = "Remote IP is taking too long to respond: 127.0.0.1";
|
||||
if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
|
||||
{
|
||||
Q_EMIT UnitTestFailed("Unit Test for Third Bad Shader Compiler Failed");
|
||||
return;
|
||||
}
|
||||
m_lastShaderCompilerErrorMessage.clear();
|
||||
Q_EMIT UnitTestPassed();
|
||||
}
|
||||
|
||||
void ShaderCompilerUnitTest::ReceiveShaderCompilerErrorMessage(QString error, QString server, QString timestamp, QString payload)
|
||||
{
|
||||
(void) server;
|
||||
(void) timestamp;
|
||||
(void) payload;
|
||||
m_lastShaderCompilerErrorMessage = error;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_UNIT_TEST(ShaderCompilerUnitTest)
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef SHADERCOMPILERUNITTEST_H
|
||||
#define SHADERCOMPILERUNITTEST_H
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "UnitTestRunner.h"
|
||||
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
#include "native/shadercompiler/shadercompilerManager.h"
|
||||
//#include "native/shadercompiler/shadercompilerMessages.h"
|
||||
#include "native/utilities/UnitTestShaderCompilerServer.h"
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#endif
|
||||
|
||||
class ConnectionManager;
|
||||
|
||||
class ShaderCompilerManagerForUnitTest : public ShaderCompilerManager
|
||||
{
|
||||
public:
|
||||
explicit ShaderCompilerManagerForUnitTest(QObject* parent = 0) : ShaderCompilerManager(parent) {};
|
||||
|
||||
// for this test, we override sendResponse and make it so that it just calls a callback instead of actually sending it to the connection manager.
|
||||
void sendResponse(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload) override
|
||||
{
|
||||
if (m_sendResponseCallbackFn)
|
||||
{
|
||||
m_sendResponseCallbackFn(connId, type, serial, payload);
|
||||
}
|
||||
}
|
||||
AZStd::function<void(unsigned int, unsigned int, unsigned int, QByteArray)> m_sendResponseCallbackFn;
|
||||
};
|
||||
|
||||
class ShaderCompilerUnitTest
|
||||
: public UnitTestRun
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShaderCompilerUnitTest();
|
||||
~ShaderCompilerUnitTest();
|
||||
virtual void StartTest() override;
|
||||
virtual int UnitTestPriority() const override;
|
||||
void ContructPayloadForShaderCompilerServer(QByteArray& payload);
|
||||
|
||||
Q_SIGNALS:
|
||||
void StartUnitTestForGoodShaderCompiler();
|
||||
void StartUnitTestForFirstBadShaderCompiler();
|
||||
void StartUnitTestForSecondBadShaderCompiler();
|
||||
void StartUnitTestForThirdBadShaderCompiler();
|
||||
|
||||
public Q_SLOTS:
|
||||
void UnitTestForGoodShaderCompiler();
|
||||
void UnitTestForFirstBadShaderCompiler();
|
||||
void UnitTestForSecondBadShaderCompiler();
|
||||
void UnitTestForThirdBadShaderCompiler();
|
||||
void VerifyPayloadForGoodShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
void VerifyPayloadForFirstBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
void VerifyPayloadForSecondBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
void VerifyPayloadForThirdBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
|
||||
void ReceiveShaderCompilerErrorMessage(QString error, QString server, QString timestamp, QString payload);
|
||||
|
||||
|
||||
private:
|
||||
UnitTestShaderCompilerServer m_server;
|
||||
ShaderCompilerManagerForUnitTest m_shaderCompilerManager;
|
||||
ConnectionManager* m_connectionManager;
|
||||
QByteArray m_testPayload;
|
||||
QString m_lastShaderCompilerErrorMessage;
|
||||
unsigned int m_connectionId = 0;
|
||||
};
|
||||
|
||||
#endif // SHADERCOMPILERUNITTEST_H
|
||||
|
||||
|
||||
@@ -231,44 +231,6 @@ bool ApplicationManager::InitiatedShutdown() const
|
||||
return m_duringShutdown;
|
||||
}
|
||||
|
||||
void ApplicationManager::GetExternalBuilderFileList(QStringList& externalBuilderModules)
|
||||
{
|
||||
externalBuilderModules.clear();
|
||||
|
||||
static const char* builder_folder_name = "Builders";
|
||||
|
||||
// LY_ASSET_BUILDERS is defined by the CMakeLists.txt. The asset builders add themselves to a variable that
|
||||
// is populated to allow selective building of those asset builder targets.
|
||||
// This allows left over Asset builders in the output directory to not be loaded by the AssetProcessor
|
||||
#if !defined(LY_ASSET_BUILDERS)
|
||||
#error LY_ASSET_BUILDERS was not defined for ApplicationManager.cpp
|
||||
#endif
|
||||
|
||||
QDir builderDir = QDir::toNativeSeparators(QString(this->m_frameworkApp.GetExecutableFolder()));
|
||||
builderDir.cd(QString(builder_folder_name));
|
||||
if (builderDir.exists())
|
||||
{
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
AZ::StringFunc::Tokenize(AZStd::string_view(LY_ASSET_BUILDERS), tokens, ',');
|
||||
AZStd::string builderLibrary;
|
||||
for (const AZStd::string& token : tokens)
|
||||
{
|
||||
QString assetBuilderPath(token.c_str());
|
||||
if (builderDir.exists(assetBuilderPath))
|
||||
{
|
||||
externalBuilderModules.push_back(builderDir.absoluteFilePath(assetBuilderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (externalBuilderModules.empty())
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor was unable to locate any external builders\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
QDir ApplicationManager::GetSystemRoot() const
|
||||
{
|
||||
return m_systemRoot;
|
||||
@@ -459,15 +421,6 @@ void ApplicationManager::PopulateApplicationDependencies()
|
||||
m_filesOfInterest.push_back(dir.absoluteFilePath(pathWithPlatformExtension));
|
||||
}
|
||||
|
||||
// Get the external builder modules to add to the files of interest
|
||||
QStringList builderModuleFileList;
|
||||
GetExternalBuilderFileList(builderModuleFileList);
|
||||
for (const QString& builderModuleFile : builderModuleFileList)
|
||||
{
|
||||
m_filesOfInterest.push_back(builderModuleFile);
|
||||
}
|
||||
|
||||
|
||||
QDir assetRoot;
|
||||
AssetUtilities::ComputeAssetRoot(assetRoot);
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "native/assetprocessor.h"
|
||||
#endif
|
||||
|
||||
class FolderWatchCallbackEx;
|
||||
class QCoreApplication;
|
||||
|
||||
namespace AZ
|
||||
@@ -139,7 +138,7 @@ protected:
|
||||
void RegisterObjectForQuit(QObject* source, bool insertInFront = false);
|
||||
bool NeedRestart() const;
|
||||
void addRunningThread(AssetProcessor::ThreadWorker* thread);
|
||||
|
||||
|
||||
template<class BuilderClass>
|
||||
void RegisterInternalBuilder(const QString& builderName);
|
||||
|
||||
@@ -151,9 +150,6 @@ protected:
|
||||
bool m_duringStartup = true;
|
||||
AssetProcessorAZApplication m_frameworkApp;
|
||||
QCoreApplication* m_qApp = nullptr;
|
||||
|
||||
//! Get the list of external builder files for this asset processor
|
||||
void GetExternalBuilderFileList(QStringList& externalBuilderModules);
|
||||
|
||||
virtual void Reflect() = 0;
|
||||
virtual const char* GetLogBaseName() = 0;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <native/InternalBuilders/SettingsRegistryBuilder.h>
|
||||
#include <AzToolsFramework/Application/Ticker.h>
|
||||
#include <AzToolsFramework/ToolsFileUtils/ToolsFileUtils.h>
|
||||
#include <AssetBuilder/AssetBuilderStatic.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -32,9 +33,6 @@
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
//! Amount of time to wait between checking the status of the AssetBuilder process
|
||||
static const int s_MaximumSleepTimeMS = 10;
|
||||
|
||||
//! CreateJobs will wait up to 2 minutes before timing out
|
||||
//! This shouldn't need to be so high but very large slices can take a while to process currently
|
||||
//! This should be reduced down to something more reasonable after slice jobs are sped up
|
||||
@@ -64,6 +62,7 @@ ApplicationManagerBase::~ApplicationManagerBase()
|
||||
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
||||
AssetProcessor::AssetBuilderRegistrationBus::Handler::BusDisconnect();
|
||||
AssetBuilderSDK::AssetBuilderBus::Handler::BusDisconnect();
|
||||
AssetProcessor::AssetBuilderInfoBus::Handler::BusDisconnect();
|
||||
|
||||
if (m_settingsRegistryBuilder)
|
||||
{
|
||||
@@ -192,7 +191,7 @@ void ApplicationManagerBase::InitAssetProcessorManager()
|
||||
{
|
||||
m_assetProcessorManager->SetEnableModtimeSkippingFeature(true);
|
||||
}
|
||||
|
||||
|
||||
if (commandLine->HasSwitch(Command_enableQueryLogging.m_switch))
|
||||
{
|
||||
m_assetProcessorManager->SetQueryLogging(true);
|
||||
@@ -206,7 +205,7 @@ void ApplicationManagerBase::InitAssetProcessorManager()
|
||||
{
|
||||
m_dependencyScanPattern = commandLine->GetSwitchValue(Command_dsp.m_switch, 0).c_str();
|
||||
}
|
||||
|
||||
|
||||
m_fileDependencyScanPattern = "*";
|
||||
|
||||
if (commandLine->HasSwitch(Command_fileDependencyScanPattern.m_switch))
|
||||
@@ -327,7 +326,7 @@ void ApplicationManagerBase::InitAssetCatalog()
|
||||
AssetProcessor::AssetCatalog* catalog = new AssetCatalog(assetCatalogHelper, m_platformConfiguration);
|
||||
|
||||
// Using a direct connection so we know the catalog has been updated before continuing on with code might depend on the asset being in the catalog
|
||||
connect(m_assetProcessorManager, &AssetProcessorManager::AssetMessage, catalog, &AssetCatalog::OnAssetMessage, Qt::DirectConnection);
|
||||
connect(m_assetProcessorManager, &AssetProcessorManager::AssetMessage, catalog, &AssetCatalog::OnAssetMessage, Qt::DirectConnection);
|
||||
connect(m_assetProcessorManager, &AssetProcessorManager::SourceQueued, catalog, &AssetCatalog::OnSourceQueued);
|
||||
connect(m_assetProcessorManager, &AssetProcessorManager::SourceFinished, catalog, &AssetCatalog::OnSourceFinished);
|
||||
connect(m_assetProcessorManager, &AssetProcessorManager::PathDependencyResolved, catalog, &AssetCatalog::OnDependencyResolved);
|
||||
@@ -379,12 +378,12 @@ void ApplicationManagerBase::InitAssetScanner()
|
||||
QObject::connect(m_assetScanner, &AssetScanner::FilesFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
|
||||
QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
|
||||
QObject::connect(m_assetScanner, &AssetScanner::ExcludedFound, [this](QSet<AssetFileInfo> files) { m_fileStateCache->AddInfoSet(files); });
|
||||
|
||||
|
||||
// file table
|
||||
QObject::connect(m_assetScanner, &AssetScanner::AssetScanningStatusChanged, m_fileProcessor.get(), &FileProcessor::OnAssetScannerStatusChange);
|
||||
QObject::connect(m_assetScanner, &AssetScanner::FilesFound, m_fileProcessor.get(), &FileProcessor::AssessFilesFromScanner);
|
||||
QObject::connect(m_assetScanner, &AssetScanner::FoldersFound, m_fileProcessor.get(), &FileProcessor::AssessFoldersFromScanner);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::DestroyAssetScanner()
|
||||
@@ -434,63 +433,77 @@ void ApplicationManagerBase::DestroyPlatformConfiguration()
|
||||
|
||||
void ApplicationManagerBase::InitFileMonitor()
|
||||
{
|
||||
m_folderWatches.reserve(m_platformConfiguration->GetScanFolderCount());
|
||||
m_watchHandles.reserve(m_platformConfiguration->GetScanFolderCount());
|
||||
for (int folderIdx = 0; folderIdx < m_platformConfiguration->GetScanFolderCount(); ++folderIdx)
|
||||
{
|
||||
const AssetProcessor::ScanFolderInfo& info = m_platformConfiguration->GetScanFolderAt(folderIdx);
|
||||
|
||||
FolderWatchCallbackEx* newFolderWatch = new FolderWatchCallbackEx(info.ScanPath(), "", info.RecurseSubFolders());
|
||||
// hook folder watcher to assess files on add/modify
|
||||
// relevant files will be sent to resource compiler
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessAddedFile);
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessModifiedFile);
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessDeletedFile);
|
||||
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [this](QString path) { m_fileStateCache->AddFile(path); });
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified, [this](QString path) { m_fileStateCache->UpdateFile(path); });
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved, [this](QString path) { m_fileStateCache->RemoveFile(path); });
|
||||
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [](QString path) { AZ::Interface<AssetProcessor::ExcludedFolderCacheInterface>::Get()->FileAdded(path); });
|
||||
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded,
|
||||
m_fileProcessor.get(), &AssetProcessor::FileProcessor::AssessAddedFile);
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
|
||||
m_fileProcessor.get(), &AssetProcessor::FileProcessor::AssessDeletedFile);
|
||||
|
||||
m_folderWatches.push_back(AZStd::unique_ptr<FolderWatchCallbackEx>(newFolderWatch));
|
||||
m_watchHandles.push_back(m_fileWatcher.AddFolderWatch(newFolderWatch));
|
||||
m_fileWatcher.AddFolderWatch(info.ScanPath(), info.RecurseSubFolders());
|
||||
}
|
||||
|
||||
// also hookup monitoring for the cache (output directory)
|
||||
QDir cacheRoot;
|
||||
if (AssetUtilities::ComputeProjectCacheRoot(cacheRoot))
|
||||
{
|
||||
FolderWatchCallbackEx* newFolderWatch = new FolderWatchCallbackEx(cacheRoot.absolutePath(), "", true);
|
||||
m_fileWatcher.AddFolderWatch(cacheRoot.absolutePath(), true);
|
||||
}
|
||||
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileAdded, [this](QString path) { m_fileStateCache->AddFile(path); });
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileModified, [this](QString path) { m_fileStateCache->UpdateFile(path); });
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved, [this](QString path) { m_fileStateCache->RemoveFile(path); });
|
||||
if (m_platformConfiguration->GetScanFolderCount() || !cacheRoot.path().isEmpty())
|
||||
{
|
||||
const auto cachePath = QDir::toNativeSeparators(cacheRoot.absolutePath());
|
||||
|
||||
// we only care about cache root deletions.
|
||||
QObject::connect(newFolderWatch, &FolderWatchCallbackEx::fileRemoved,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssessDeletedFile);
|
||||
const auto OnFileAdded = [this, cachePath](QString path)
|
||||
{
|
||||
const bool isCacheRoot = path.startsWith(cachePath);
|
||||
if (isCacheRoot)
|
||||
{
|
||||
m_fileStateCache->AddFile(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assetProcessorManager->AssessAddedFile(path);
|
||||
m_fileStateCache->AddFile(path);
|
||||
AZ::Interface<AssetProcessor::ExcludedFolderCacheInterface>::Get()->FileAdded(path);
|
||||
m_fileProcessor->AssetProcessor::FileProcessor::AssessAddedFile(path);
|
||||
}
|
||||
};
|
||||
|
||||
m_folderWatches.push_back(AZStd::unique_ptr<FolderWatchCallbackEx>(newFolderWatch));
|
||||
m_watchHandles.push_back(m_fileWatcher.AddFolderWatch(newFolderWatch));
|
||||
const auto OnFileModified = [this, cachePath](QString path)
|
||||
{
|
||||
const bool isCacheRoot = path.startsWith(cachePath);
|
||||
if (isCacheRoot)
|
||||
{
|
||||
m_assetProcessorManager->AssessModifiedFile(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assetProcessorManager->AssessModifiedFile(path);
|
||||
m_fileStateCache->UpdateFile(path);
|
||||
}
|
||||
};
|
||||
|
||||
const auto OnFileRemoved = [this, cachePath](QString path)
|
||||
{
|
||||
const bool isCacheRoot = path.startsWith(cachePath);
|
||||
if (isCacheRoot)
|
||||
{
|
||||
m_fileStateCache->RemoveFile(path);
|
||||
m_assetProcessorManager->AssessDeletedFile(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assetProcessorManager->AssessDeletedFile(path);
|
||||
m_fileStateCache->RemoveFile(path);
|
||||
m_fileProcessor->AssessDeletedFile(path);
|
||||
}
|
||||
};
|
||||
|
||||
connect(&m_fileWatcher, &FileWatcher::fileAdded, OnFileAdded);
|
||||
connect(&m_fileWatcher, &FileWatcher::fileModified, OnFileModified);
|
||||
connect(&m_fileWatcher, &FileWatcher::fileRemoved, OnFileRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::DestroyFileMonitor()
|
||||
{
|
||||
for (int watchHandle : m_watchHandles)
|
||||
{
|
||||
m_fileWatcher.RemoveFolderWatch(watchHandle);
|
||||
}
|
||||
m_folderWatches.resize(0);
|
||||
m_fileWatcher.ClearFolderWatches();
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::DestroyApplicationServer()
|
||||
@@ -591,6 +604,51 @@ void ApplicationManagerBase::InitConnectionManager()
|
||||
}, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4)
|
||||
);
|
||||
|
||||
m_connectionManager->RegisterService(
|
||||
AssetBuilder::BuilderRegistrationRequest::MessageType,
|
||||
[this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload, QString)
|
||||
{
|
||||
AssetBuilder::BuilderRegistrationRequest registrationRequest;
|
||||
|
||||
if (m_builderRegistrationComplete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_builderRegistrationComplete = true;
|
||||
|
||||
if (AssetProcessor::UnpackMessage(payload, registrationRequest))
|
||||
{
|
||||
for (const auto& builder : registrationRequest.m_builders)
|
||||
{
|
||||
AssetBuilderSDK::AssetBuilderDesc desc;
|
||||
desc.m_name = builder.m_name;
|
||||
desc.m_patterns = builder.m_patterns;
|
||||
desc.m_version = builder.m_version;
|
||||
desc.m_analysisFingerprint = builder.m_analysisFingerprint;
|
||||
desc.m_flags = builder.m_flags;
|
||||
desc.m_busId = builder.m_busId;
|
||||
desc.m_flagsByJobKey = builder.m_flagsByJobKey;
|
||||
desc.m_productsToKeepOnFailure = builder.m_productsToKeepOnFailure;
|
||||
|
||||
// Builders registered this way are always external builders
|
||||
desc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::External;
|
||||
|
||||
RegisterBuilderInformation(desc);
|
||||
}
|
||||
|
||||
QTimer::singleShot(
|
||||
0, this,
|
||||
[this]()
|
||||
{
|
||||
if (!PostActivate())
|
||||
{
|
||||
QuitRequested();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//You can get Asset Processor Current State
|
||||
using AzFramework::AssetSystem::RequestAssetProcessorStatus;
|
||||
auto GetState = [this](unsigned int connId, unsigned int, unsigned int serial, QByteArray payload, QString)
|
||||
@@ -633,11 +691,11 @@ void ApplicationManagerBase::InitConnectionManager()
|
||||
AssetProcessorPlatformStatusRequest requestMessage;
|
||||
if (AssetProcessor::UnpackMessage(payload, requestMessage))
|
||||
{
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(responseMessage.m_isPlatformEnabled,
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(responseMessage.m_isPlatformEnabled,
|
||||
&AzToolsFramework::AssetSystemRequestBus::Events::IsAssetPlatformEnabled, requestMessage.m_platform.c_str());
|
||||
}
|
||||
|
||||
AssetProcessor::ConnectionBus::Event(connId,
|
||||
AssetProcessor::ConnectionBus::Event(connId,
|
||||
&AssetProcessor::ConnectionBus::Events::SendResponse, serial, responseMessage);
|
||||
});
|
||||
|
||||
@@ -653,11 +711,11 @@ void ApplicationManagerBase::InitConnectionManager()
|
||||
if (AssetProcessor::UnpackMessage(payload, requestMessage))
|
||||
{
|
||||
const char* platformIdentifier = requestMessage.m_platform.c_str();
|
||||
responseMessage.m_numberOfPendingJobs =
|
||||
responseMessage.m_numberOfPendingJobs =
|
||||
GetRCController()->NumberOfPendingJobsPerPlatform(platformIdentifier);
|
||||
}
|
||||
|
||||
AssetProcessor::ConnectionBus::Event(connId,
|
||||
AssetProcessor::ConnectionBus::Event(connId,
|
||||
&AssetProcessor::ConnectionBus::Events::SendResponse, serial, responseMessage);
|
||||
});
|
||||
}
|
||||
@@ -696,7 +754,7 @@ void ApplicationManagerBase::InitAssetRequestHandler(AssetProcessor::AssetReques
|
||||
QObject::connect(GetAssetProcessorManager(), &AssetProcessorManager::SendAssetExistsResponse, m_assetRequestHandler, &AssetRequestHandler::OnRequestAssetExistsResponse);
|
||||
|
||||
QObject::connect(GetAssetProcessorManager(), &AssetProcessorManager::FenceFileDetected, m_assetRequestHandler, &AssetRequestHandler::OnFenceFileDetected);
|
||||
|
||||
|
||||
// connect the Asset Request Handler to RC:
|
||||
QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestCompileGroup, GetRCController(), &RCController::OnRequestCompileGroup);
|
||||
QObject::connect(m_assetRequestHandler, &AssetRequestHandler::RequestEscalateAssetBySearchTerm, GetRCController(), &RCController::OnEscalateJobsBySearchTerm);
|
||||
@@ -754,8 +812,6 @@ ApplicationManager::BeforeRunStatus ApplicationManagerBase::BeforeRun()
|
||||
qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AzFramework::AssetSystem::AssetStatus");
|
||||
qRegisterMetaType<AzFramework::AssetSystem::AssetStatus>("AssetStatus");
|
||||
|
||||
qRegisterMetaType<FileChangeInfo>("FileChangeInfo");
|
||||
|
||||
qRegisterMetaType<AssetProcessor::AssetScanningStatus>("AssetScanningStatus");
|
||||
|
||||
qRegisterMetaType<AssetProcessor::NetworkRequestID>("NetworkRequestID");
|
||||
@@ -840,14 +896,6 @@ bool ApplicationManagerBase::Run()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool startedSuccessfully = true;
|
||||
|
||||
if (!PostActivate())
|
||||
{
|
||||
QuitRequested();
|
||||
startedSuccessfully = false;
|
||||
}
|
||||
|
||||
AZ_Printf(AssetProcessor::ConsoleChannel, "Asset Processor Batch Processing Started.\n");
|
||||
AZ_Printf(AssetProcessor::ConsoleChannel, "-----------------------------------------\n");
|
||||
QElapsedTimer allAssetsProcessingTimer;
|
||||
@@ -867,7 +915,7 @@ bool ApplicationManagerBase::Run()
|
||||
RemoveOldTempFolders();
|
||||
Destroy();
|
||||
|
||||
return (startedSuccessfully && FailedAssetsCount() == 0);
|
||||
return FailedAssetsCount() == 0;
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::HandleFileRelocation() const
|
||||
@@ -899,7 +947,7 @@ void ApplicationManagerBase::HandleFileRelocation() const
|
||||
while(!m_sourceControlReady)
|
||||
{
|
||||
// We need to wait for source control to be ready before continuing
|
||||
|
||||
|
||||
if (printCounter % 10 == 0)
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Waiting for Source Control connection\n");
|
||||
@@ -966,18 +1014,18 @@ void ApplicationManagerBase::HandleFileRelocation() const
|
||||
AZ_Printf(AssetProcessor::ConsoleChannel, "SETTING: Preview file move. Run again with --%s to actually make changes\n", ConfirmCommand);
|
||||
}
|
||||
|
||||
auto* interface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
|
||||
auto* relocationInterface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
|
||||
|
||||
if(interface)
|
||||
if(relocationInterface)
|
||||
{
|
||||
auto result = interface->Move(source, destination, previewOnly, allowBrokenDependencies, !leaveEmptyFolders, updateReferences, excludeMetaDataFiles);
|
||||
auto result = relocationInterface->Move(source, destination, previewOnly, allowBrokenDependencies, !leaveEmptyFolders, updateReferences, excludeMetaDataFiles);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
AssetProcessor::RelocationSuccess success = result.TakeValue();
|
||||
|
||||
// The report can be too long for the AZ_Printf buffer, so split it into individual lines
|
||||
AZStd::string report = interface->BuildReport(success.m_relocationContainer, success.m_updateTasks, true, updateReferences);
|
||||
AZStd::string report = relocationInterface->BuildReport(success.m_relocationContainer, success.m_updateTasks, true, updateReferences);
|
||||
AZStd::vector<AZStd::string> lines;
|
||||
AzFramework::StringFunc::Tokenize(report.c_str(), lines, "\n");
|
||||
|
||||
@@ -1051,18 +1099,18 @@ void ApplicationManagerBase::HandleFileRelocation() const
|
||||
AZ_Printf(AssetProcessor::ConsoleChannel, "SETTING: Preview file delete. Run again with --%s to actually make changes\n", ConfirmCommand);
|
||||
}
|
||||
|
||||
auto* interface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
|
||||
auto* relocationInterface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
|
||||
|
||||
if (interface)
|
||||
if (relocationInterface)
|
||||
{
|
||||
auto result = interface->Delete(source, previewOnly, allowBrokenDependencies, !leaveEmptyFolders, excludeMetaDataFiles);
|
||||
auto result = relocationInterface->Delete(source, previewOnly, allowBrokenDependencies, !leaveEmptyFolders, excludeMetaDataFiles);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
AssetProcessor::RelocationSuccess success = result.TakeValue();
|
||||
|
||||
// The report can be too long for the AZ_Printf buffer, so split it into individual lines
|
||||
AZStd::string report = interface->BuildReport(success.m_relocationContainer, success.m_updateTasks, false, updateReferences);
|
||||
AZStd::string report = relocationInterface->BuildReport(success.m_relocationContainer, success.m_updateTasks, false, updateReferences);
|
||||
AZStd::vector<AZStd::string> lines;
|
||||
AzFramework::StringFunc::Tokenize(report.c_str(), lines, "\n");
|
||||
|
||||
@@ -1129,7 +1177,7 @@ void ApplicationManagerBase::CheckForIdle()
|
||||
TryScanProductDependencies();
|
||||
|
||||
TryHandleFileRelocation();
|
||||
|
||||
|
||||
// since we are shutting down, we save the registry and then we quit.
|
||||
AZ_Printf(AssetProcessor::ConsoleChannel, "No assets remain in the build queue. Saving the catalog, and then shutting down.\n");
|
||||
// stop accepting any further idle messages, as we will shut down - don't want this function to repeat!
|
||||
@@ -1173,7 +1221,7 @@ void ApplicationManagerBase::InitBuilderManager()
|
||||
{
|
||||
m_builderManager->ConnectionLost(connId);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::ShutdownBuilderManager()
|
||||
@@ -1207,7 +1255,7 @@ void ApplicationManagerBase::ShutDownAssetDatabase()
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::InitFileProcessor()
|
||||
void ApplicationManagerBase::InitFileProcessor()
|
||||
{
|
||||
AssetProcessor::ThreadController<AssetProcessor::FileProcessor>* fileProcessorHelper = new AssetProcessor::ThreadController<AssetProcessor::FileProcessor>();
|
||||
|
||||
@@ -1298,22 +1346,13 @@ bool ApplicationManagerBase::Activate()
|
||||
}
|
||||
|
||||
InitBuilderConfiguration();
|
||||
|
||||
m_isCurrentlyLoadingGems = true;
|
||||
if (!ActivateModules())
|
||||
{
|
||||
// ActivateModules reports any errors it encounters.
|
||||
m_isCurrentlyLoadingGems = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_isCurrentlyLoadingGems = false;
|
||||
PopulateApplicationDependencies();
|
||||
|
||||
InitAssetProcessorManager();
|
||||
AssetBuilderSDK::InitializeSerializationContext();
|
||||
AssetBuilderSDK::InitializeBehaviorContext();
|
||||
|
||||
AssetBuilder::InitializeSerializationContext();
|
||||
|
||||
InitFileStateCache();
|
||||
InitFileProcessor();
|
||||
|
||||
@@ -1341,7 +1380,7 @@ bool ApplicationManagerBase::Activate()
|
||||
RegisterObjectForQuit(m_rcController);
|
||||
|
||||
m_connectionsToRemoveOnShutdown << QObject::connect(
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessorManagerIdleState,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessorManagerIdleState,
|
||||
this, [this](bool state)
|
||||
{
|
||||
if (state)
|
||||
@@ -1362,7 +1401,7 @@ bool ApplicationManagerBase::Activate()
|
||||
});
|
||||
|
||||
m_connectionsToRemoveOnShutdown << QObject::connect(
|
||||
this, &ApplicationManagerBase::CheckAssetProcessorManagerIdleState,
|
||||
this, &ApplicationManagerBase::CheckAssetProcessorManagerIdleState,
|
||||
m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::CheckAssetProcessorIdleState);
|
||||
|
||||
MakeActivationConnections();
|
||||
@@ -1376,6 +1415,22 @@ bool ApplicationManagerBase::Activate()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Initializing_Builders, 0, QString());
|
||||
Q_EMIT AssetProcessorStatusChanged(entry);
|
||||
|
||||
AZStd::thread_desc desc;
|
||||
desc.m_name = "Builder Component Registration";
|
||||
AZStd::thread builderRegistrationThread(
|
||||
desc,
|
||||
[]()
|
||||
{
|
||||
AssetProcessor::BuilderRef builder;
|
||||
AssetProcessor::BuilderManagerBus::BroadcastResult(builder, &AssetProcessor::BuilderManagerBus::Events::GetBuilder, true);
|
||||
});
|
||||
|
||||
builderRegistrationThread.detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1384,11 +1439,6 @@ bool ApplicationManagerBase::PostActivate()
|
||||
m_connectionManager->LoadConnections();
|
||||
|
||||
InitializeInternalBuilders();
|
||||
if (!InitializeExternalBuilders())
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "AssetProcessor is closing. Failed to initialize and load all the external builders. Please ensure that Builders_Temp directory is not read-only. Please see log for more information.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_EMIT OnBuildersRegistered();
|
||||
|
||||
@@ -1401,7 +1451,7 @@ bool ApplicationManagerBase::PostActivate()
|
||||
AZ::SystemTickBus::Broadcast(&AZ::SystemTickEvents::OnSystemTick);
|
||||
});
|
||||
|
||||
// now that everything is up and running, we start scanning. Before this, we don't want file events to start percolating through the
|
||||
// now that everything is up and running, we start scanning. Before this, we don't want file events to start percolating through the
|
||||
// asset system.
|
||||
|
||||
GetAssetScanner()->StartScan();
|
||||
@@ -1425,124 +1475,20 @@ bool ApplicationManagerBase::InitializeInternalBuilders()
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ApplicationManagerBase::InitializeExternalBuilders()
|
||||
{
|
||||
AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Initializing_Builders);
|
||||
Q_EMIT AssetProcessorStatusChanged(entry);
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents);
|
||||
|
||||
|
||||
// Get the list of external build modules (full paths)
|
||||
QStringList fileList;
|
||||
GetExternalBuilderFileList(fileList);
|
||||
|
||||
for (const QString& filePath : fileList)
|
||||
{
|
||||
if (QLibrary::isLibrary(filePath))
|
||||
{
|
||||
AssetProcessor::ExternalModuleAssetBuilderInfo* externalAssetBuilderInfo = new AssetProcessor::ExternalModuleAssetBuilderInfo(filePath);
|
||||
AssetProcessor::AssetBuilderType assetBuilderType = externalAssetBuilderInfo->Load();
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "AssetProcessor is loading library %s\n", filePath.toUtf8().data());
|
||||
if (assetBuilderType == AssetProcessor::AssetBuilderType::None)
|
||||
{
|
||||
AZ_Warning(AssetProcessor::DebugChannel, false, "Non-builder DLL was found in Builders directory %s, skipping. \n", filePath.toUtf8().data());
|
||||
delete externalAssetBuilderInfo;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assetBuilderType == AssetProcessor::AssetBuilderType::Invalid)
|
||||
{
|
||||
AZ_Warning(AssetProcessor::DebugChannel, false, "AssetProcessor was not able to load the library: %s\n", filePath.toUtf8().data());
|
||||
delete externalAssetBuilderInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Initializing and registering builder %s\n", externalAssetBuilderInfo->GetName().toUtf8().data());
|
||||
|
||||
m_currentExternalAssetBuilder = externalAssetBuilderInfo;
|
||||
|
||||
externalAssetBuilderInfo->Initialize();
|
||||
|
||||
m_currentExternalAssetBuilder = nullptr;
|
||||
|
||||
m_externalAssetBuilders.push_back(externalAssetBuilderInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Also init external builders which may be inside of Gems
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::CreateAndAddEntityFromComponentTags,
|
||||
AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }), "AssetBuilders Entity");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplicationManagerBase::WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds)
|
||||
{
|
||||
AZ::u32 exitCode = 0;
|
||||
bool finishedOK = false;
|
||||
QElapsedTimer ticker;
|
||||
ProcessCommunicatorTracePrinter tracer(processWatcher->GetCommunicator(), "AssetBuilder");
|
||||
|
||||
ticker.start();
|
||||
|
||||
while (!finishedOK)
|
||||
{
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(s_MaximumSleepTimeMS));
|
||||
|
||||
tracer.Pump();
|
||||
|
||||
if (ticker.elapsed() > processTimeoutLimitInSeconds * 1000 || (jobCancelListener && jobCancelListener->IsCancelled()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!processWatcher->IsProcessRunning(&exitCode))
|
||||
{
|
||||
finishedOK = true; // we either cant wait for it, or it finished.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tracer.Pump(); // empty whats left if possible.
|
||||
|
||||
if (processWatcher->IsProcessRunning(&exitCode))
|
||||
{
|
||||
processWatcher->TerminateProcess(1);
|
||||
}
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "AssetBuilder exited with error code %d", exitCode);
|
||||
return false;
|
||||
}
|
||||
else if (jobCancelListener && jobCancelListener->IsCancelled())
|
||||
{
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "AssetBuilder was terminated. There was a request to cancel the job.\n");
|
||||
return false;
|
||||
}
|
||||
else if (!finishedOK)
|
||||
{
|
||||
AZ_Error(AssetProcessor::ConsoleChannel, false, "AssetBuilder failed to terminate within %d seconds", processTimeoutLimitInSeconds);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::RegisterBuilderInformation(const AssetBuilderSDK::AssetBuilderDesc& builderDesc)
|
||||
{
|
||||
// Create Job Function validation
|
||||
AZ_Error(AssetProcessor::ConsoleChannel,
|
||||
builderDesc.m_createJobFunction,
|
||||
"Create Job Function (m_createJobFunction) for %s builder is empty.\n",
|
||||
builderDesc.m_name.c_str());
|
||||
if (!builderDesc.IsExternalBuilder())
|
||||
{
|
||||
// Create Job Function validation
|
||||
AZ_Error(
|
||||
AssetProcessor::ConsoleChannel, builderDesc.m_createJobFunction,
|
||||
"Create Job Function (m_createJobFunction) for %s builder is empty.\n", builderDesc.m_name.c_str());
|
||||
|
||||
// Process Job Function validation
|
||||
AZ_Error(AssetProcessor::ConsoleChannel,
|
||||
builderDesc.m_processJobFunction,
|
||||
"Process Job Function (m_processJobFunction) for %s builder is empty.\n",
|
||||
builderDesc.m_name.c_str());
|
||||
// Process Job Function validation
|
||||
AZ_Error(
|
||||
AssetProcessor::ConsoleChannel, builderDesc.m_processJobFunction,
|
||||
"Process Job Function (m_processJobFunction) for %s builder is empty.\n", builderDesc.m_name.c_str());
|
||||
}
|
||||
|
||||
// Bus ID validation
|
||||
AZ_Error(AssetProcessor::ConsoleChannel,
|
||||
@@ -1550,67 +1496,66 @@ void ApplicationManagerBase::RegisterBuilderInformation(const AssetBuilderSDK::A
|
||||
"Bus ID for %s builder is empty.\n",
|
||||
builderDesc.m_name.c_str());
|
||||
|
||||
// This is an external builder registering, we will want to track its builder desc since it can register multiple ones
|
||||
AZStd::string builderFilePath;
|
||||
if (m_currentExternalAssetBuilder)
|
||||
{
|
||||
m_currentExternalAssetBuilder->RegisterBuilderDesc(builderDesc.m_busId);
|
||||
builderFilePath = m_currentExternalAssetBuilder->GetModuleFullPath().toUtf8().data();
|
||||
}
|
||||
|
||||
AssetBuilderSDK::AssetBuilderDesc modifiedBuilderDesc = builderDesc;
|
||||
// Allow for overrides defined in a BuilderConfig.ini file to update our code defined default values
|
||||
AssetProcessor::BuilderConfigurationRequestBus::Broadcast(&AssetProcessor::BuilderConfigurationRequests::UpdateBuilderDescriptor, builderDesc.m_name, modifiedBuilderDesc);
|
||||
|
||||
if (builderDesc.IsExternalBuilder())
|
||||
{
|
||||
// We're going to override the createJob function so we can run it externally in AssetBuilder, rather than having it run inside the AP
|
||||
modifiedBuilderDesc.m_createJobFunction = [builderFilePath](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
|
||||
// We're going to override the createJob function so we can run it externally in AssetBuilder, rather than having it run
|
||||
// inside the AP
|
||||
modifiedBuilderDesc.m_createJobFunction =
|
||||
[](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
|
||||
{
|
||||
AssetProcessor::BuilderRef builderRef;
|
||||
AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder, false);
|
||||
|
||||
if (builderRef)
|
||||
{
|
||||
AssetProcessor::BuilderRef builderRef;
|
||||
AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder);
|
||||
int retryCount = 0;
|
||||
AssetProcessor::BuilderRunJobOutcome result;
|
||||
|
||||
if (builderRef)
|
||||
do
|
||||
{
|
||||
int retryCount = 0;
|
||||
AssetProcessor::BuilderRunJobOutcome result;
|
||||
|
||||
do
|
||||
{
|
||||
retryCount++;
|
||||
result = builderRef->RunJob<AssetBuilderSDK::CreateJobsNetRequest, AssetBuilderSDK::CreateJobsNetResponse>(request, response, s_MaximumCreateJobsTimeSeconds, "create", builderFilePath, nullptr);
|
||||
} while (result == AssetProcessor::BuilderRunJobOutcome::LostConnection && retryCount <= AssetProcessor::RetriesForJobNetworkError);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
|
||||
}
|
||||
};
|
||||
retryCount++;
|
||||
result = builderRef->RunJob<AssetBuilder::CreateJobsNetRequest, AssetBuilder::CreateJobsNetResponse>(
|
||||
request, response, s_MaximumCreateJobsTimeSeconds, "create", "", nullptr);
|
||||
} while (result == AssetProcessor::BuilderRunJobOutcome::LostConnection &&
|
||||
retryCount <= AssetProcessor::RetriesForJobNetworkError);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
|
||||
}
|
||||
};
|
||||
|
||||
// Also override the processJob function to run externally
|
||||
modifiedBuilderDesc.m_processJobFunction = [builderFilePath](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
|
||||
modifiedBuilderDesc.m_processJobFunction =
|
||||
[](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
|
||||
AssetProcessor::BuilderRef builderRef;
|
||||
AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder, false);
|
||||
|
||||
if (builderRef)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
int retryCount = 0;
|
||||
AssetProcessor::BuilderRunJobOutcome result;
|
||||
|
||||
AssetProcessor::BuilderRef builderRef;
|
||||
AssetProcessor::BuilderManagerBus::BroadcastResult(builderRef, &AssetProcessor::BuilderManagerBusTraits::GetBuilder);
|
||||
|
||||
if (builderRef)
|
||||
do
|
||||
{
|
||||
int retryCount = 0;
|
||||
AssetProcessor::BuilderRunJobOutcome result;
|
||||
|
||||
do
|
||||
{
|
||||
retryCount++;
|
||||
result = builderRef->RunJob<AssetBuilderSDK::ProcessJobNetRequest, AssetBuilderSDK::ProcessJobNetResponse>(request, response, s_MaximumProcessJobsTimeSeconds, "process", builderFilePath, &jobCancelListener, request.m_tempDirPath);
|
||||
} while (result == AssetProcessor::BuilderRunJobOutcome::LostConnection && retryCount <= AssetProcessor::RetriesForJobNetworkError);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
|
||||
}
|
||||
};
|
||||
retryCount++;
|
||||
result = builderRef->RunJob<AssetBuilder::ProcessJobNetRequest, AssetBuilder::ProcessJobNetResponse>(
|
||||
request, response, s_MaximumProcessJobsTimeSeconds, "process", "", &jobCancelListener, request.m_tempDirPath);
|
||||
} while (result == AssetProcessor::BuilderRunJobOutcome::LostConnection &&
|
||||
retryCount <= AssetProcessor::RetriesForJobNetworkError);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AssetProcessor", false, "Failed to retrieve a valid builder to process job");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (m_builderDescMap.find(modifiedBuilderDesc.m_busId) != m_builderDescMap.end())
|
||||
@@ -1768,7 +1713,7 @@ bool ApplicationManagerBase::CheckSufficientDiskSpace(const QString& savePath, q
|
||||
[[maybe_unused]] bool result = AzToolsFramework::ToolsFileUtils::GetFreeDiskSpace(savePath, bytesFree);
|
||||
|
||||
AZ_Assert(result, "Unable to determine the amount of free space on drive containing path (%s).", savePath.toUtf8().constData());
|
||||
|
||||
|
||||
if (bytesFree < requiredSpace + s_ReservedDiskSpaceInBytes)
|
||||
{
|
||||
if (shutdownIfInsufficient)
|
||||
@@ -1806,8 +1751,8 @@ void ApplicationManagerBase::RemoveOldTempFolders()
|
||||
return;
|
||||
}
|
||||
|
||||
// We will remove old temp folders if either their modified time is older than the cutoff time or
|
||||
// if the total number of temp folders have exceeded the maximum number of temp folders.
|
||||
// We will remove old temp folders if either their modified time is older than the cutoff time or
|
||||
// if the total number of temp folders have exceeded the maximum number of temp folders.
|
||||
QFileInfoList entries = root.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time); // sorting by modification time
|
||||
int folderCount = 0;
|
||||
bool removeFolder = false;
|
||||
@@ -1821,9 +1766,9 @@ void ApplicationManagerBase::RemoveOldTempFolders()
|
||||
|
||||
// Since we are sorting the folders list from latest to oldest, we will either be in a state where we have to delete all the remaining folders or not
|
||||
// because either we have reached the folder limit or reached the cutoff date limit.
|
||||
removeFolder = removeFolder || (folderCount++ >= s_MaximumTempFolders) ||
|
||||
removeFolder = removeFolder || (folderCount++ >= s_MaximumTempFolders) ||
|
||||
(entry.lastModified() < cutoffTime);
|
||||
|
||||
|
||||
if (removeFolder)
|
||||
{
|
||||
QDir dir(entry.absoluteFilePath());
|
||||
@@ -1837,8 +1782,6 @@ void ApplicationManagerBase::ConnectivityStateChanged(const AzToolsFramework::So
|
||||
Q_EMIT SourceControlReady();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ApplicationManagerBase::OnAssetProcessorManagerIdleState(bool isIdle)
|
||||
{
|
||||
// these can come in during shutdown.
|
||||
|
||||
@@ -47,7 +47,6 @@ namespace AssetProcessor
|
||||
|
||||
class ApplicationServer;
|
||||
class ConnectionManager;
|
||||
class FolderWatchCallbackEx;
|
||||
class ControlRequestHandler;
|
||||
|
||||
class ApplicationManagerBase
|
||||
@@ -149,7 +148,6 @@ protected:
|
||||
void CreateQtApplication() override;
|
||||
|
||||
bool InitializeInternalBuilders();
|
||||
bool InitializeExternalBuilders();
|
||||
void InitBuilderManager();
|
||||
void ShutdownBuilderManager();
|
||||
bool InitAssetDatabase();
|
||||
@@ -173,8 +171,6 @@ protected:
|
||||
|
||||
AssetProcessor::AssetCatalog* GetAssetCatalog() const { return m_assetCatalog; }
|
||||
|
||||
static bool WaitForBuilderExit(AzFramework::ProcessWatcher* processWatcher, AssetBuilderSDK::JobCancelListener* jobCancelListener, AZ::u32 processTimeoutLimitInSeconds);
|
||||
|
||||
ApplicationServer* m_applicationServer = nullptr;
|
||||
ConnectionManager* m_connectionManager = nullptr;
|
||||
|
||||
@@ -195,9 +191,7 @@ protected:
|
||||
bool m_sourceControlReady = false;
|
||||
bool m_fullIdle = false;
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<FolderWatchCallbackEx> > m_folderWatches;
|
||||
FileWatcher m_fileWatcher;
|
||||
AZStd::vector<int> m_watchHandles;
|
||||
AssetProcessor::PlatformConfiguration* m_platformConfiguration = nullptr;
|
||||
AssetProcessor::AssetProcessorManager* m_assetProcessorManager = nullptr;
|
||||
AssetProcessor::AssetCatalog* m_assetCatalog = nullptr;
|
||||
@@ -218,6 +212,8 @@ protected:
|
||||
AZStd::shared_ptr<AssetProcessor::InternalRecognizerBasedBuilder> m_internalBuilder;
|
||||
AZStd::shared_ptr<AssetProcessor::SettingsRegistryBuilder> m_settingsRegistryBuilder;
|
||||
|
||||
bool m_builderRegistrationComplete = false;
|
||||
|
||||
// Builder description map based on the builder id
|
||||
AZStd::unordered_map<AZ::Uuid, AssetBuilderSDK::AssetBuilderDesc> m_builderDescMap;
|
||||
|
||||
@@ -231,7 +227,7 @@ protected:
|
||||
AZStd::list<AssetProcessor::ExternalModuleAssetBuilderInfo*> m_externalAssetBuilders;
|
||||
|
||||
AssetProcessor::ExternalModuleAssetBuilderInfo* m_currentExternalAssetBuilder = nullptr;
|
||||
|
||||
|
||||
QAtomicInt m_connectionsAwaitingAssetCatalogSave = 0;
|
||||
int m_remainingAPMJobs = 0;
|
||||
bool m_assetProcessorManagerIsReady = false;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <native/resourcecompiler/RCBuilder.h>
|
||||
#include <AssetBuilder/AssetBuilderInfo.h>
|
||||
|
||||
class FolderWatchCallbackEx;
|
||||
class QCoreApplication;
|
||||
|
||||
namespace AssetProcessor
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <native/utilities/AssetBuilderInfo.h>
|
||||
#include <QCoreApplication>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AssetBuilder/AssetBuilderStatic.h>
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
@@ -138,7 +139,7 @@ namespace AssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
bool Builder::Start()
|
||||
bool Builder::Start(bool doRegistration)
|
||||
{
|
||||
// Get the current BinXXX folder based on the current running AP
|
||||
QString applicationDir = QCoreApplication::instance()->applicationDirPath();
|
||||
@@ -155,7 +156,7 @@ namespace AssetProcessor
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string> params = BuildParams("resident", buildersFolder.c_str(), UuidString(), "", "");
|
||||
const AZStd::vector<AZStd::string> params = BuildParams("resident", buildersFolder.c_str(), UuidString(), "", "", doRegistration);
|
||||
|
||||
m_processWatcher = LaunchProcess(fullExePathString.c_str(), params);
|
||||
|
||||
@@ -179,7 +180,7 @@ namespace AssetProcessor
|
||||
return !m_processWatcher || (m_processWatcher && m_processWatcher->IsProcessRunning(exitCode));
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> Builder::BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile) const
|
||||
AZStd::vector<AZStd::string> Builder::BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile, bool doRegistration) const
|
||||
{
|
||||
QDir projectCacheRoot;
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
@@ -200,6 +201,11 @@ namespace AssetProcessor
|
||||
params.emplace_back(AZStd::string::format(R"(-engine-path="%s")", enginePath.c_str()));
|
||||
params.emplace_back(AZStd::string::format("-port=%d", portNumber));
|
||||
|
||||
if(doRegistration)
|
||||
{
|
||||
params.emplace_back("--register");
|
||||
}
|
||||
|
||||
if (moduleFilePath && moduleFilePath[0])
|
||||
{
|
||||
params.emplace_back(AZStd::string::format(R"(-module="%s")", moduleFilePath));
|
||||
@@ -232,7 +238,7 @@ namespace AssetProcessor
|
||||
{
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_processExecutableString = fullExePath;
|
||||
|
||||
|
||||
AZStd::vector<AZStd::string> commandLineArray{ fullExePath };
|
||||
commandLineArray.insert(commandLineArray.end(), params.begin(), params.end());
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::move(commandLineArray);
|
||||
@@ -350,17 +356,19 @@ namespace AssetProcessor
|
||||
BuilderManager::BuilderManager(ConnectionManager* connectionManager)
|
||||
{
|
||||
using namespace AZStd::placeholders;
|
||||
connectionManager->RegisterService(AssetBuilderSDK::BuilderHelloRequest::MessageType(), AZStd::bind(&BuilderManager::IncomingBuilderPing, this, _1, _2, _3, _4, _5));
|
||||
connectionManager->RegisterService(AssetBuilder::BuilderHelloRequest::MessageType(), AZStd::bind(&BuilderManager::IncomingBuilderPing, this, _1, _2, _3, _4, _5));
|
||||
|
||||
// Setup a background thread to pump the idle builders so they don't get blocked trying to output to stdout/err
|
||||
m_pollingThread = AZStd::thread([this]()
|
||||
AZStd::thread_desc desc;
|
||||
desc.m_name = "BuilderManager Idle Pump";
|
||||
m_pollingThread = AZStd::thread(desc, [this]()
|
||||
{
|
||||
while (!m_quitListener.WasQuitRequested())
|
||||
{
|
||||
while (!m_quitListener.WasQuitRequested())
|
||||
{
|
||||
PumpIdleBuilders();
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(s_IdleBuilderPumpingDelayMS));
|
||||
}
|
||||
});
|
||||
PumpIdleBuilders();
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(s_IdleBuilderPumpingDelayMS));
|
||||
}
|
||||
});
|
||||
|
||||
m_quitListener.BusConnect();
|
||||
BusConnect();
|
||||
@@ -399,8 +407,8 @@ namespace AssetProcessor
|
||||
|
||||
void BuilderManager::IncomingBuilderPing(AZ::u32 connId, AZ::u32 /*type*/, AZ::u32 serial, QByteArray payload, QString platform)
|
||||
{
|
||||
AssetBuilderSDK::BuilderHelloRequest requestPing;
|
||||
AssetBuilderSDK::BuilderHelloResponse responsePing;
|
||||
AssetBuilder::BuilderHelloRequest requestPing;
|
||||
AssetBuilder::BuilderHelloResponse responsePing;
|
||||
|
||||
if (!AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.length(), requestPing))
|
||||
{
|
||||
@@ -476,7 +484,7 @@ namespace AssetProcessor
|
||||
return builder;
|
||||
}
|
||||
|
||||
BuilderRef BuilderManager::GetBuilder()
|
||||
BuilderRef BuilderManager::GetBuilder(bool doRegistration)
|
||||
{
|
||||
AZStd::shared_ptr<Builder> newBuilder;
|
||||
BuilderRef builderRef;
|
||||
@@ -484,27 +492,30 @@ namespace AssetProcessor
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_buildersMutex);
|
||||
|
||||
for (auto itr = m_builders.begin(); itr != m_builders.end(); )
|
||||
if (!doRegistration)
|
||||
{
|
||||
auto& builder = itr->second;
|
||||
|
||||
if (!builder->m_busy)
|
||||
for (auto itr = m_builders.begin(); itr != m_builders.end();)
|
||||
{
|
||||
builder->PumpCommunicator();
|
||||
auto& builder = itr->second;
|
||||
|
||||
if (builder->IsValid())
|
||||
if (!builder->m_busy)
|
||||
{
|
||||
return BuilderRef(builder);
|
||||
builder->PumpCommunicator();
|
||||
|
||||
if (builder->IsValid())
|
||||
{
|
||||
return BuilderRef(builder);
|
||||
}
|
||||
else
|
||||
{
|
||||
itr = m_builders.erase(itr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
itr = m_builders.erase(itr);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_TracePrintf("BuilderManager", "Starting new builder for job request\n");
|
||||
@@ -516,7 +527,7 @@ namespace AssetProcessor
|
||||
builderRef = BuilderRef(newBuilder);
|
||||
}
|
||||
|
||||
if (!newBuilder->Start())
|
||||
if (!newBuilder->Start(doRegistration))
|
||||
{
|
||||
AZ_Error("BuilderManager", false, "Builder failed to start");
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AssetProcessor
|
||||
virtual ~BuilderManagerBusTraits() = default;
|
||||
|
||||
//! Returns a builder for doing work
|
||||
virtual BuilderRef GetBuilder() = 0;
|
||||
virtual BuilderRef GetBuilder(bool doRegistration) = 0;
|
||||
};
|
||||
|
||||
using BuilderManagerBus = AZ::EBus<BuilderManagerBusTraits>;
|
||||
@@ -98,12 +98,12 @@ namespace AssetProcessor
|
||||
private:
|
||||
|
||||
//! Starts the builder process and waits for it to connect
|
||||
bool Start();
|
||||
bool Start(bool doRegistration);
|
||||
|
||||
//! Sets the connection id and signals that the builder has connected
|
||||
void SetConnection(AZ::u32 connId);
|
||||
|
||||
AZStd::vector<AZStd::string> BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile) const;
|
||||
AZStd::vector<AZStd::string> BuildParams(const char* task, const char* moduleFilePath, const AZStd::string& builderGuid, const AZStd::string& jobDescriptionFile, const AZStd::string& jobResponseFile, bool doRegistration) const;
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> LaunchProcess(const char* fullExePath, const AZStd::vector<AZStd::string>& params) const;
|
||||
|
||||
//! Waits for the builder exe to send the job response and pumps stdout/err
|
||||
@@ -169,7 +169,7 @@ namespace AssetProcessor
|
||||
void ConnectionLost(AZ::u32 connId);
|
||||
|
||||
//BuilderManagerBus
|
||||
BuilderRef GetBuilder() override;
|
||||
BuilderRef GetBuilder(bool doRegistration) override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace AssetProcessor
|
||||
|
||||
if (!netResponse.m_response.Succeeded() || s_createRequestFileForSuccessfulJob)
|
||||
{
|
||||
// we write the request out to disk for failure or debugging
|
||||
// we write the request out to disk for failure or debugging
|
||||
if (!DebugWriteRequestFile(tempFolderPath.c_str(), request, task, modulePath))
|
||||
{
|
||||
return BuilderRunJobOutcome::FailedToWriteDebugRequest;
|
||||
@@ -83,7 +83,7 @@ namespace AssetProcessor
|
||||
return false;
|
||||
}
|
||||
|
||||
auto params = BuildParams(task.c_str(), modulePath.c_str(), "", jobRequestFile, jobResponseFile);
|
||||
auto params = BuildParams(task.c_str(), modulePath.c_str(), "", jobRequestFile, jobResponseFile, false);
|
||||
AZStd::string paramString;
|
||||
AZ::StringFunc::Join(paramString, params.begin(), params.end(), " ");
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include "native/resourcecompiler/rccontroller.h"
|
||||
#include "native/FileServer/fileServer.h"
|
||||
#include "native/AssetManager/assetScanner.h"
|
||||
#include "native/shadercompiler/shadercompilerManager.h"
|
||||
#include "native/shadercompiler/shadercompilerModel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDialogButtonBox>
|
||||
@@ -55,7 +53,7 @@ namespace
|
||||
{
|
||||
moduleFileInfo.setFile(executableDirectory);
|
||||
}
|
||||
|
||||
|
||||
QDir binaryDir = moduleFileInfo.absoluteDir();
|
||||
// strip extension
|
||||
QString applicationBase = moduleFileInfo.completeBaseName();
|
||||
@@ -70,7 +68,7 @@ namespace
|
||||
binaryDir.remove(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -145,8 +143,6 @@ void GUIApplicationManager::Destroy()
|
||||
|
||||
DestroyIniConfiguration();
|
||||
DestroyFileServer();
|
||||
DestroyShaderCompilerManager();
|
||||
DestroyShaderCompilerModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +188,7 @@ bool GUIApplicationManager::Run()
|
||||
wrapper->enableSaveRestoreGeometry(GetOrganizationName(), GetApplicationName(), "MainWindow", restoreOnFirstShow);
|
||||
|
||||
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow, QStringLiteral("style:AssetProcessor.qss"));
|
||||
|
||||
|
||||
auto refreshStyleSheets = [styleManager]()
|
||||
{
|
||||
styleManager->Refresh();
|
||||
@@ -322,19 +318,11 @@ bool GUIApplicationManager::Run()
|
||||
|
||||
qApp->setQuitOnLastWindowClosed(false);
|
||||
|
||||
QTimer::singleShot(0, this, [this]()
|
||||
{
|
||||
if (!PostActivate())
|
||||
{
|
||||
QuitRequested();
|
||||
m_startedSuccessfully = false;
|
||||
}
|
||||
});
|
||||
|
||||
m_duringStartup = false;
|
||||
m_startedSuccessfully = true;
|
||||
|
||||
int resultCode = qApp->exec(); // this blocks until the last window is closed.
|
||||
|
||||
|
||||
if(!InitiatedShutdown())
|
||||
{
|
||||
// if we are here it implies that AP did not stop the Qt event loop and is shutting down prematurely
|
||||
@@ -427,7 +415,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're the main thread, then consider showing the message box directly.
|
||||
// If we're the main thread, then consider showing the message box directly.
|
||||
// note that all other threads will PAUSE if they emit a message while the main thread is showing this box
|
||||
// due to the way the trace system EBUS is mutex-protected.
|
||||
Qt::ConnectionType connection = Qt::DirectConnection;
|
||||
@@ -470,7 +458,7 @@ bool GUIApplicationManager::Activate()
|
||||
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
|
||||
m_localUserSettings.Load(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context);
|
||||
m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
|
||||
|
||||
|
||||
InitIniConfiguration();
|
||||
InitFileServer();
|
||||
|
||||
@@ -479,9 +467,6 @@ bool GUIApplicationManager::Activate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InitShaderCompilerModel();
|
||||
InitShaderCompilerManager();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -490,6 +475,7 @@ bool GUIApplicationManager::PostActivate()
|
||||
{
|
||||
if (!ApplicationManagerBase::PostActivate())
|
||||
{
|
||||
m_startedSuccessfully = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -606,7 +592,7 @@ void GUIApplicationManager::InitConnectionManager()
|
||||
QObject::connect(m_fileServer, SIGNAL(AddRenameRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRenameRequest(unsigned int, bool)));
|
||||
QObject::connect(m_fileServer, SIGNAL(AddFindFileNamesRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFindFileNamesRequest(unsigned int, bool)));
|
||||
QObject::connect(m_fileServer, SIGNAL(UpdateConnectionMetrics()), m_connectionManager, SLOT(UpdateConnectionMetrics()));
|
||||
|
||||
|
||||
m_connectionManager->RegisterService(ShowAssetProcessorRequest::MessageType,
|
||||
std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray /*payload*/)
|
||||
{
|
||||
@@ -661,40 +647,6 @@ void GUIApplicationManager::DestroyFileServer()
|
||||
}
|
||||
}
|
||||
|
||||
void GUIApplicationManager::InitShaderCompilerManager()
|
||||
{
|
||||
m_shaderCompilerManager = new ShaderCompilerManager();
|
||||
|
||||
//Shader compiler stuff
|
||||
m_connectionManager->RegisterService(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), std::bind(&ShaderCompilerManager::process, m_shaderCompilerManager, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
QObject::connect(m_shaderCompilerManager, SIGNAL(sendErrorMessageFromShaderJob(QString, QString, QString, QString)), m_shaderCompilerModel, SLOT(addShaderErrorInfoEntry(QString, QString, QString, QString)));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GUIApplicationManager::DestroyShaderCompilerManager()
|
||||
{
|
||||
if (m_shaderCompilerManager)
|
||||
{
|
||||
delete m_shaderCompilerManager;
|
||||
m_shaderCompilerManager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GUIApplicationManager::InitShaderCompilerModel()
|
||||
{
|
||||
m_shaderCompilerModel = new ShaderCompilerModel();
|
||||
}
|
||||
|
||||
void GUIApplicationManager::DestroyShaderCompilerModel()
|
||||
{
|
||||
if (m_shaderCompilerModel)
|
||||
{
|
||||
delete m_shaderCompilerModel;
|
||||
m_shaderCompilerModel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
IniConfiguration* GUIApplicationManager::GetIniConfiguration() const
|
||||
{
|
||||
return m_iniConfiguration;
|
||||
@@ -704,14 +656,6 @@ FileServer* GUIApplicationManager::GetFileServer() const
|
||||
{
|
||||
return m_fileServer;
|
||||
}
|
||||
ShaderCompilerManager* GUIApplicationManager::GetShaderCompilerManager() const
|
||||
{
|
||||
return m_shaderCompilerManager;
|
||||
}
|
||||
ShaderCompilerModel* GUIApplicationManager::GetShaderCompilerModel() const
|
||||
{
|
||||
return m_shaderCompilerModel;
|
||||
}
|
||||
|
||||
void GUIApplicationManager::ShowTrayIconErrorMessage(QString msg)
|
||||
{
|
||||
|
||||
@@ -24,8 +24,6 @@ class ConnectionManager;
|
||||
class IniConfiguration;
|
||||
class ApplicationServer;
|
||||
class FileServer;
|
||||
class ShaderCompilerManager;
|
||||
class ShaderCompilerModel;
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
@@ -47,8 +45,6 @@ public:
|
||||
ApplicationManager::BeforeRunStatus BeforeRun() override;
|
||||
IniConfiguration* GetIniConfiguration() const;
|
||||
FileServer* GetFileServer() const;
|
||||
ShaderCompilerManager* GetShaderCompilerManager() const;
|
||||
ShaderCompilerModel* GetShaderCompilerModel() const;
|
||||
|
||||
bool Run() override;
|
||||
////////////////////////////////////////////////////
|
||||
@@ -72,10 +68,6 @@ private:
|
||||
void DestroyIniConfiguration();
|
||||
void InitFileServer();
|
||||
void DestroyFileServer();
|
||||
void InitShaderCompilerManager();
|
||||
void DestroyShaderCompilerManager();
|
||||
void InitShaderCompilerModel();
|
||||
void DestroyShaderCompilerModel();
|
||||
void Destroy() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
@@ -99,8 +91,7 @@ private:
|
||||
|
||||
IniConfiguration* m_iniConfiguration = nullptr;
|
||||
FileServer* m_fileServer = nullptr;
|
||||
ShaderCompilerManager* m_shaderCompilerManager = nullptr;
|
||||
ShaderCompilerModel* m_shaderCompilerModel = nullptr;
|
||||
|
||||
QFileSystemWatcher m_qtFileWatcher;
|
||||
AZ::UserSettingsProvider m_localUserSettings;
|
||||
bool m_messageBoxIsVisible = false;
|
||||
|
||||
Reference in New Issue
Block a user