void FShaderParameterMap::VerifyBindingsAreComplete(const TCHAR* ShaderTypeName, EShaderFrequency Frequency, FVertexFactoryType* InVertexFactoryType) const { #if WITH_EDITORONLY_DATA // Only people working on shaders (and therefore have LogShaders unsuppressed) will want to see these errors if (UE_LOG_ACTIVE(LogShaders, Warning)) { const TCHAR* VertexFactoryName = InVertexFactoryType ? InVertexFactoryType->GetName() : TEXT("?"); bool bBindingsComplete = true; FString UnBoundParameters = TEXT(""); for (TMap<FString,FParameterAllocation>::TConstIterator ParameterIt(ParameterMap);ParameterIt;++ParameterIt) { const FString& ParamName = ParameterIt.Key(); const FParameterAllocation& ParamValue = ParameterIt.Value(); if(!ParamValue.bBound) { // Only valid parameters should be in the shader map checkSlow(ParamValue.Size > 0); bBindingsComplete = bBindingsComplete && ParamValue.bBound; UnBoundParameters += FString(TEXT(" Parameter ")) + ParamName + TEXT(" not bound!\n"); } } if (!bBindingsComplete) { FString ErrorMessage = FString(TEXT("Found unbound parameters being used in shadertype ")) + ShaderTypeName + TEXT(" (VertexFactory: ") + VertexFactoryName + TEXT(")\n") + UnBoundParameters; // An unbound parameter means the engine is not going to set its value (because it was never bound) // but it will be used in rendering, which will most likely cause artifacts // We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate. FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *ErrorMessage, TEXT("Error")); } } #endif // WITH_EDITORONLY_DATA }
void FShaderUniformBufferParameter::Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags) { uint16 UnusedBaseIndex = 0; uint16 UnusedNumBytes = 0; #if UE_BUILD_DEBUG bInitialized = true; #endif if(!ParameterMap.FindParameterAllocation(ParameterName,BaseIndex,UnusedBaseIndex,UnusedNumBytes)) { bIsBound = false; if(Flags == SPF_Mandatory) { if (!UE_LOG_ACTIVE(LogShaders, Log)) { UE_LOG(LogShaders, Fatal,TEXT("Failure to bind non-optional shader resource parameter %s! The parameter is either not present in the shader, or the shader compiler optimized it out."),ParameterName); } else { // We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate. FPlatformMisc::MessageBoxExt( EAppMsgType::Ok, *FText::Format( NSLOCTEXT("UnrealEd", "Error_FailedToBindShaderParameter", "Failure to bind non-optional shader parameter {0}! The parameter is either not present in the shader, or the shader compiler optimized it out. This will be an assert with LogShaders suppressed!"), FText::FromString(ParameterName)).ToString(), TEXT("Warning")); } } } else { bIsBound = true; } }
/** * Stores derived data in the DDC. * @param DerivedData - The data to store in the DDC. * @param DerivedDataKeySuffix - The key suffix at which to store derived data. */ static void PutDerivedDataInCache( FStreamedAudioPlatformData* DerivedData, const FString& DerivedDataKeySuffix ) { TArray<uint8> RawDerivedData; FString DerivedDataKey; // Build the key with which to cache derived data. GetStreamedAudioDerivedDataKeyFromSuffix(DerivedDataKeySuffix, DerivedDataKey); FString LogString; if (UE_LOG_ACTIVE(LogAudio,Verbose)) { LogString = FString::Printf( TEXT("Storing Streamed Audio in DDC:\n Key: %s\n Format: %s\n"), *DerivedDataKey, *DerivedData->AudioFormat.ToString() ); } // Write out individual chunks to the derived data cache. const int32 ChunkCount = DerivedData->Chunks.Num(); for (int32 ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex) { FString ChunkDerivedDataKey; FStreamedAudioChunk& Chunk = DerivedData->Chunks[ChunkIndex]; GetStreamedAudioDerivedChunkKey(ChunkIndex, Chunk, DerivedDataKeySuffix, ChunkDerivedDataKey); if (UE_LOG_ACTIVE(LogAudio,Verbose)) { LogString += FString::Printf(TEXT(" Chunk%d %d bytes %s\n"), ChunkIndex, Chunk.BulkData.GetBulkDataSize(), *ChunkDerivedDataKey ); } Chunk.StoreInDerivedDataCache(ChunkDerivedDataKey); } // Store derived data. FMemoryWriter Ar(RawDerivedData, /*bIsPersistent=*/ true); DerivedData->Serialize(Ar, NULL); GetDerivedDataCacheRef().Put(*DerivedDataKey, RawDerivedData); UE_LOG(LogAudio,Verbose,TEXT("%s Derived Data: %d bytes"),*LogString,RawDerivedData.Num()); }
bool FStreamingWaveData::UpdateStreamingStatus() { bool bHasPendingRequestInFlight = true; int32 RequestStatus = PendingChunkChangeRequestStatus.GetValue(); TArray<uint32> IndicesToLoad; TArray<uint32> IndicesToFree; if (!HasPendingRequests(IndicesToLoad, IndicesToFree)) { check(RequestStatus == AudioState_ReadyFor_Requests); bHasPendingRequestInFlight = false; } // Pending request in flight, though we might be able to finish it. else { if (RequestStatus == AudioState_ReadyFor_Finalization) { if (UE_LOG_ACTIVE(LogAudio, Log) && IndicesToLoad.Num() > 0) { FString LogString = FString::Printf(TEXT("Finalised loading of chunk(s) %d"), IndicesToLoad[0]); for (int32 Index = 1; Index < IndicesToLoad.Num(); ++ Index) { LogString += FString::Printf(TEXT(", %d"), IndicesToLoad[Index]); } LogString += FString::Printf(TEXT(" from SoundWave'%s'"), *SoundWave->GetName()); UE_LOG(LogAudio, Log, TEXT("%s"), *LogString); } bool bFailedRequests = false; #if WITH_EDITORONLY_DATA bFailedRequests = FinishDDCRequests(); #endif //WITH_EDITORONLY_DATA PendingChunkChangeRequestStatus.Decrement(); bHasPendingRequestInFlight = false; LoadedChunkIndices = CurrentRequest.RequiredIndices; } else if (RequestStatus == AudioState_ReadyFor_Requests) { BeginPendingRequests(IndicesToLoad, IndicesToFree); } } return bHasPendingRequestInFlight; }
/** Blocks the CPU to synchronize with vblank by communicating with DWM. */ void FD3D11Viewport::PresentWithVsyncDWM() { #if D3D11_WITH_DWMAPI LARGE_INTEGER Cycles; DWM_TIMING_INFO TimingInfo; // Find out how long since we last flipped and query DWM for timing information. QueryPerformanceCounter(&Cycles); FMemory::MemZero(TimingInfo); TimingInfo.cbSize = sizeof(DWM_TIMING_INFO); DwmGetCompositionTimingInfo(WindowHandle, &TimingInfo); uint64 QpcAtFlip = Cycles.QuadPart; uint64 CyclesSinceLastFlip = Cycles.QuadPart - LastFlipTime; float CPUTime = FPlatformTime::ToMilliseconds(CyclesSinceLastFlip); float GPUTime = FPlatformTime::ToMilliseconds(TimingInfo.qpcFrameComplete - LastCompleteTime); float DisplayRefreshPeriod = FPlatformTime::ToMilliseconds(TimingInfo.qpcRefreshPeriod); // Find the smallest multiple of the refresh rate that is >= 33ms, our target frame rate. float RefreshPeriod = DisplayRefreshPeriod; if (RHIConsoleVariables::bForceThirtyHz && RefreshPeriod > 1.0f) { while (RefreshPeriod - (1000.0f / 30.0f) < -1.0f) { RefreshPeriod *= 2.0f; } } // If the last frame hasn't completed yet, we don't know how long the GPU took. bool bValidGPUTime = (TimingInfo.cFrameComplete > LastFrameComplete); if (bValidGPUTime) { GPUTime /= (float)(TimingInfo.cFrameComplete - LastFrameComplete); } // Update the sync counter depending on how much time it took to complete the previous frame. float FrameTime = FMath::Max<float>(CPUTime, GPUTime); if (FrameTime >= RHIConsoleVariables::SyncRefreshThreshold * RefreshPeriod) { SyncCounter--; } else if (bValidGPUTime) { SyncCounter++; } SyncCounter = FMath::Clamp<int32>(SyncCounter, 0, RHIConsoleVariables::MaxSyncCounter); // If frames are being completed quickly enough, block for vsync. bool bSync = (SyncCounter >= RHIConsoleVariables::SyncThreshold); if (bSync) { // This flushes the previous present call and blocks until it is made available to DWM. D3DRHI->GetDeviceContext()->Flush(); DwmFlush(); // We sleep a percentage of the remaining time. The trick is to get the // present call in after the vblank we just synced for but with time to // spare for the next vblank. float MinFrameTime = RefreshPeriod * RHIConsoleVariables::RefreshPercentageBeforePresent; float TimeToSleep; do { QueryPerformanceCounter(&Cycles); float TimeSinceFlip = FPlatformTime::ToMilliseconds(Cycles.QuadPart - LastFlipTime); TimeToSleep = (MinFrameTime - TimeSinceFlip); if (TimeToSleep > 0.0f) { FPlatformProcess::Sleep(TimeToSleep * 0.001f); } } while (TimeToSleep > 0.0f); } // Present. PresentChecked(/*SyncInterval=*/ 0); // If we are forcing <= 30Hz, block the CPU an additional amount of time if needed. // This second block is only needed when RefreshPercentageBeforePresent < 1.0. if (bSync) { LARGE_INTEGER LocalCycles; float TimeToSleep; bool bSaveCycles = false; do { QueryPerformanceCounter(&LocalCycles); float TimeSinceFlip = FPlatformTime::ToMilliseconds(LocalCycles.QuadPart - LastFlipTime); TimeToSleep = (RefreshPeriod - TimeSinceFlip); if (TimeToSleep > 0.0f) { bSaveCycles = true; FPlatformProcess::Sleep(TimeToSleep * 0.001f); } } while (TimeToSleep > 0.0f); if (bSaveCycles) { Cycles = LocalCycles; } } // If we are dropping vsync reset the counter. This provides a debounce time // before which we try to vsync again. if (!bSync && bSyncedLastFrame) { SyncCounter = 0; } if (bSync != bSyncedLastFrame || UE_LOG_ACTIVE(LogRHI,VeryVerbose)) { UE_LOG(LogRHI,Verbose,TEXT("BlockForVsync[%d]: CPUTime:%.2fms GPUTime[%d]:%.2fms Blocked:%.2fms Pending/Complete:%d/%d"), bSync, CPUTime, bValidGPUTime, GPUTime, FPlatformTime::ToMilliseconds(Cycles.QuadPart - QpcAtFlip), TimingInfo.cFramePending, TimingInfo.cFrameComplete); } // Remember if we synced, when the frame completed, etc. bSyncedLastFrame = bSync; LastFlipTime = Cycles.QuadPart; LastFrameComplete = TimingInfo.cFrameComplete; LastCompleteTime = TimingInfo.qpcFrameComplete; #endif //D3D11_WITH_DWMAPI }
void FStreamingWaveData::BeginPendingRequests(const TArray<uint32>& IndicesToLoad, const TArray<uint32>& IndicesToFree) { if (UE_LOG_ACTIVE(LogAudio, Log) && IndicesToLoad.Num() > 0) { FString LogString = FString::Printf(TEXT("Requesting ASync load of chunk(s) %d"), IndicesToLoad[0]); for (int32 Index = 1; Index < IndicesToLoad.Num(); ++Index) { LogString += FString::Printf(TEXT(", %d"), IndicesToLoad[Index]); } LogString += FString::Printf(TEXT(" from SoundWave'%s'"), *SoundWave->GetName()); UE_LOG(LogAudio, Log, TEXT("%s"), *LogString); } // Mark Chunks for removal in case they can be reused TArray<uint32> FreeChunkIndices; for (auto Index : IndicesToFree) { for (int32 ChunkIndex = 0; ChunkIndex < LoadedChunks.Num(); ++ChunkIndex) { if (LoadedChunks[ChunkIndex].Index == Index) { FreeChunkIndices.AddUnique(ChunkIndex); break; } } } if (IndicesToLoad.Num() > 0) { PendingChunkChangeRequestStatus.Set(AudioState_InProgress_Loading); // Set off all IO Requests for (auto Index : IndicesToLoad) { const FStreamedAudioChunk& Chunk = SoundWave->RunningPlatformData->Chunks[Index]; int32 ChunkSize = Chunk.DataSize; FLoadedAudioChunk* ChunkStorage = NULL; for (auto FreeIndex : FreeChunkIndices) { if (LoadedChunks[FreeIndex].MemorySize >= ChunkSize) { FreeChunkIndices.Remove(FreeIndex); ChunkStorage = &LoadedChunks[FreeIndex]; ChunkStorage->DataSize = ChunkSize; ChunkStorage->Index = Index; break; } } if (ChunkStorage == NULL) { ChunkStorage = AddNewLoadedChunk(ChunkSize); ChunkStorage->Index = Index; } // Pass the request on to the async io manager after increasing the request count. The request count // has been pre-incremented before fielding the update request so we don't have to worry about file // I/O immediately completing and the game thread kicking off again before this function // returns. PendingChunkChangeRequestStatus.Increment(); EAsyncIOPriority AsyncIOPriority = CurrentRequest.bPrioritiseRequest ? AIOP_BelowNormal : AIOP_Low; // Load and decompress async. #if WITH_EDITORONLY_DATA if (Chunk.DerivedDataKey.IsEmpty() == false) { FAsyncStreamDerivedChunkTask* Task = new(PendingAsyncStreamDerivedChunkTasks)FAsyncStreamDerivedChunkTask( Chunk.DerivedDataKey, ChunkStorage->Data, ChunkSize, &PendingChunkChangeRequestStatus ); Task->StartBackgroundTask(); } else #endif // #if WITH_EDITORONLY_DATA { check(Chunk.BulkData.GetFilename().Len()); if (Chunk.BulkData.IsStoredCompressedOnDisk()) { IORequestIndices.AddUnique(FIOSystem::Get().LoadCompressedData( Chunk.BulkData.GetFilename(), // filename Chunk.BulkData.GetBulkDataOffsetInFile(), // offset Chunk.BulkData.GetBulkDataSizeOnDisk(), // compressed size Chunk.BulkData.GetBulkDataSize(), // uncompressed size ChunkStorage->Data, // dest pointer Chunk.BulkData.GetDecompressionFlags(), // compressed data format &PendingChunkChangeRequestStatus, // counter to decrement AsyncIOPriority // priority ) ); } // Load async. else { IORequestIndices.AddUnique(FIOSystem::Get().LoadData( Chunk.BulkData.GetFilename(), // filename Chunk.BulkData.GetBulkDataOffsetInFile(), // offset Chunk.BulkData.GetBulkDataSize(), // size ChunkStorage->Data, // dest pointer &PendingChunkChangeRequestStatus, // counter to decrement AsyncIOPriority // priority ) ); } check(IORequestIndices[IORequestIndices.Num() - 1]); } } // Decrement the state to AudioState_InProgress_Loading + NumChunksCurrentLoading - 1. PendingChunkChangeRequestStatus.Decrement(); } else { // Skip straight to finalisation PendingChunkChangeRequestStatus.Set(AudioState_ReadyFor_Finalization); } // Ensure indices are in order so we can step through backwards FreeChunkIndices.Sort(); for (int32 FreeIndex = FreeChunkIndices.Num() - 1; FreeIndex >= 0; --FreeIndex) { FreeLoadedChunk(LoadedChunks[FreeChunkIndices[FreeIndex]]); LoadedChunks.RemoveAt(FreeChunkIndices[FreeIndex]); } }