Files
UnrealEngineUWP/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLDebugFrameDump.cpp
Marc Audy 5ce407d808 Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 3358467)
#rb none
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3297108 on 2017/02/10 by Mieszko.Zielinski

	Added AISight's peripherial vision angle claming as well as marked up UI to not allow values from outside of [0,180] range #UE4

	#jira UE-41114

Change 3299467 on 2017/02/13 by Marc.Audy

	Don't try to update active sounds on audio thread if the audio component is not active. If these functions are callled from a constructor on an async loading thread it can cause a crash

Change 3300692 on 2017/02/13 by Marc.Audy

	no auto

Change 3301424 on 2017/02/14 by Marc.Audy

	Handle gateway expansion before the node matching loop
	#jira UE-41858

Change 3301547 on 2017/02/14 by Marc.Audy

	PR #3246: Added BindDelegate that supports functions with custom (static) arguments (Contributed by PhoenixBlack)
	#jira UE-41926

Change 3301557 on 2017/02/14 by Marc.Audy

	When passing null to Rename for the new name, maintain the OldName is possible
	#jira UE-41937

Change 3301676 on 2017/02/14 by Marc.Audy

	Fix pending occlusion async traces from crashing during shutdown
	#jira UE-41939

Change 3302705 on 2017/02/14 by Mieszko.Zielinski

	Removed 'PRAGMA_DISABLE_OPTIMIZATION' uccurences from AIModule #UE4

Change 3302898 on 2017/02/14 by Dan.Oconnor

	Fix double negative

Change 3302954 on 2017/02/14 by Dan.Oconnor

	Make sure we use a good version of the class

Change 3302977 on 2017/02/14 by Dan.Oconnor

	Optimization in reinstancer turned back on - 3302898 has fixed the regression

Change 3302984 on 2017/02/14 by Dan.Oconnor

	Relink classes that were not recompiled in a wave of the compilation manager - currently only happens for data only blueprints.

	This fixes issues in Odin when using the compilation  manager

Change 3303824 on 2017/02/15 by Richard.Hinckley

	Updating URL for FABRIK system information.

Change 3304284 on 2017/02/15 by Dan.Oconnor

	Build fix

Change 3304297 on 2017/02/15 by Dan.Oconnor

	Shadow variable fix

Change 3304465 on 2017/02/15 by Lukasz.Furman

	fixed handling pathfollowing's requests by FloatingPawnMovement
	#jira UE-41884

Change 3305031 on 2017/02/15 by Marc.Audy

	All objects should get PostLoadSubobjects calls, regardless of whether they are outered to a CDO or not
	#jira UE-41708

Change 3305505 on 2017/02/15 by Michael.Noland

	Blueprints: Fix a crash when opening a BP with a parent class that no longer exists (unguarded access to the parent class)

Change 3305506 on 2017/02/15 by Michael.Noland

	QAGame: Created some assets that reference a non-existent type to test 'gracefully' handling missing native class types

Change 3306091 on 2017/02/16 by Marc.Audy

	PR #3263: Fixed duplicate comment from OnAudioFinished (Contributed by FrostByteGER)
	#jira UE-42027

Change 3306574 on 2017/02/16 by Marc.Audy

	Linked To pins can belong to invalid nodes and fail to load, this shouldn't be considered fatal

Change 3307160 on 2017/02/16 by Marc.Audy

	Rename(null, null ... is sometimes used to just force a name out of the way, so in that case don't try and maintain old name.

Change 3307982 on 2017/02/16 by Michael.Noland

	QAGame: Added another test asset for missing classes (this time a missing node class placed in a BP)

Change 3308097 on 2017/02/16 by Michael.Noland

	Graph Editor: Instantly clear GraphNodeForMenu and GraphPinForMenu as soon as the menu is dismissed, fixing crashes and other odd issues after deleting pins
	#jira UE-41789

Change 3308303 on 2017/02/16 by Dan.Oconnor

	Make sure we don't call GetDefaultObject while compiling on a non-native class

Change 3308850 on 2017/02/17 by Mieszko.Zielinski

	Fully exposed NavModifierVolume as ENGINE_API #UE4

Change 3309624 on 2017/02/17 by Phillip.Kavan

	[UE-40443] Recursively emit ctor initialization code for nested default subobjects when nativizing a Blueprint class.

	change summary:
	- modified FEmitDefaultValueHelper::OuterGenerate() to recursively detect and handle nested default subobjects.

	#jira UE-40443

Change 3310475 on 2017/02/17 by Dan.Oconnor

	Split bluepint compilation into CompileClassLayout and CompileFunctions, fix class hierarchy after creating reinstancers in blueprintcompilation manager. Together this means we don't need to run RecompileBlueprintBytecode

Change 3310487 on 2017/02/17 by Dan.Oconnor

	Fix build error missed by preflgiht

Change 3310497 on 2017/02/17 by Dan.Oconnor

	More build fixes for things missed by preflight...

Change 3310635 on 2017/02/17 by Dan.Oconnor

	Remove unused parameter and add comment to blueprint compilation manager explaining abuse of bBeingCompiled

Change 3310639 on 2017/02/17 by Dan.Oconnor

	Shadow variable fixes, not sure why these are being detected now

Change 3311855 on 2017/02/20 by Marc.Audy

	Fix UChildActorComponent::ParentComponent being null on the client
	#jira UE-42140

Change 3312444 on 2017/02/20 by Marc.Audy

	Add a bAutoDestroy pin to BP Spawn Sound and Force Feedback nodes to allow users to reuse the created component
	#jira UE-41267

Change 3312691 on 2017/02/20 by mason.seay

	Deleting map now that bug has been fixed

Change 3312709 on 2017/02/20 by Phillip.Kavan

	[UE-39705] Fix broken collision shapes when cooking with optimized BP component data option.

	change summary:
	- modified FComponentInstancingDataUtils::RecursivePropertyGather() to exclude deprecated properties, since they won't be serialized on save.
	- modified FBlueprintCookedComponentInstancingData::LoadCachedPropertyDataForSerialization() to remove the PPF_UseDeprecatedProperties flag (these are no longer included in the delta).
	- modified UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides() to remove the PPF_UseDeprecatedProperties flag (was being incorrectly used here).
	- modified AActor::CreateComponentFromTemplateData() to remove the PPF_UseDeprecatedProperties flag (was being incorrectly used here; this caused deprecated property defaults to be copied out to the instance).
	- modified AActor::CreateComponentFromTemplateData() to append RF_PostLoad/RF_NeedPostLoadSubobjects and call PostDuplicate()/ConditionalPostLoad() on the new instance (needed to mirror what SDO does in the unoptimized case - for proper physics RB setup specifically, but may be other areas where that's needed).

	#jira UE-39705

Change 3313161 on 2017/02/20 by Mieszko.Zielinski

	PR #3272: Use Pawn for GetNavAgentPropertiesRef(). (Contributed by drelidan7)

Change 3314151 on 2017/02/21 by Mieszko.Zielinski

	fix to hlods complaining about missing nav collision in cooked builds #UE4

	Made sure hlod-generated StaticMeshes are marked as not having navigation data

	#jira UE-42034

Change 3314355 on 2017/02/21 by Marc.Audy

	Set error message back to be correctly about mobility
	#jira UE-42209

Change 3314566 on 2017/02/21 by Phillip.Kavan

	[UE-40801] Switch to an ensure() to potentially help diagnose a one-off assertion crash in the SCS editor if encountered again in a future release.

	#jira UE-40801

Change 3315459 on 2017/02/21 by Mike.Beach

	Updated marquee selection in graph editors. Ctrl dragging now inverts nodes' selection state (not only deselects them - holding alt is now for only deselection).

	#jira UE-16359

Change 3315546 on 2017/02/21 by Mike.Beach

	Mirroring CL 3294552

	Count "GeneratedStub" as a success for cooked file generation - ensures the saved asset gets recorded in the asset registry.

	#jira ODIN-5869

Change 3315554 on 2017/02/21 by Mike.Beach

	Do not generate NativizedAsset plugin files if no Blueprints were nativized (cut down on mod generate/cook time).

	#jira ODIN-6211

Change 3317225 on 2017/02/22 by mason.seay

	Enable Net Use Owner Frequency on blueprints.  This allows the client to use different weapons. Doesn't fix UE-42017 though.

Change 3317495 on 2017/02/22 by Marc.Audy

	Expose raw input device configurations to other modules by request

	#jira UE-42204

Change 3319966 on 2017/02/23 by Nick.Atamas

	Polished up the material reroute node:
	 - Removed some unnecessary widgets
	 - Centered the pin node

Change 3320099 on 2017/02/23 by Mike.Beach

	Guarding against passing self pins to referance parameters (it is not a property that is referencable, and would crash). Notifying the user through pin connection messages, and providing a script exception.

	#jira UE-40861

Change 3321227 on 2017/02/24 by Marc.Audy

	Just use name rather than going Name -> String -> TCHAR -> Name

Change 3321425 on 2017/02/24 by Marc.Audy

	Minor optimizations to avoid string construction when doing StaticFindObject and ResolveName

Change 3321630 on 2017/02/24 by Mike.Beach

	Removing reference notation from actor pointer param - allowing you to pass 'self' to Blueprint exposed function.

Change 3321845 on 2017/02/24 by Lukasz.Furman

	fixed navlink processor trace accepting only components with WorldStatic object type
	#ue4

Change 3322474 on 2017/02/24 by Aaron.McLeran

	UE-42345 Rewriting thumbnail renderer

Change 3322490 on 2017/02/24 by Aaron.McLeran

	UE-42345  Forgot to take abs of sample before averaging

Change 3323562 on 2017/02/27 by Mike.Beach

	Fixing bad merge, copying loop from //UE4/Main that accidently got replaced.

Change 3323685 on 2017/02/27 by Mike.Beach

	Preventing us from cross-binding editor & PIE actors when we fixup level script actor bindings (on duplicate for PIE).

	#jira UE-30816

Change 3323776 on 2017/02/27 by Marc.Audy

	Coding standard clean up pass

Change 3324050 on 2017/02/27 by Ben.Zeigler

	Fix issue with a StreamableHandle being cancelled while in progress leaving the in progress flag active. Added and improved error messages when streaming goes wrong
	Port of 3317217, 3315540, and 3314374 from UE4-Fortnite

Change 3324294 on 2017/02/27 by Ben.Zeigler

	Engine changes needed to support "Asset Management" UI:
	Add concept of "Manage" dependency to the Asset Registry, to represent that an asset like a texture is managed by a Primary Asset. This will be used to compute usage statistics and chunking
	Add ability for AssetManager to override the PrimaryAssetType/Id on a asset data loaded off disk. Needed so the asset audit tools work properly
	Significant performance improvements to the asset registry dependency gather, and correctly report as in progress while dependencies are still being gathered. On Fortnite it now finishes in 10 seconds instead of 100
	Add bUpdateDiskCacheAfterLoad option for the asset registry, if true (default) this will update the Asset Registry's disk cache when an object is loaded, only in the editor. This is so changes made in PostLoad are correctly mirrored in the disk cache
	Add PrimaryAssetType as a wrapper struct around FName to allow customizations and blueprint usage, clean up the noexport definitions for a few related classes
	Add Asset Manager code to create and query "Manage" references used for auditing and chunking
	Add code to read AssetManager scanning rules out of the AssetManagerSettings object, also settable in editor
	Made it so UWorlds are now PrimaryAssets of the type Map, and enable the AssetManager by default for all games
	Port of CL #3323720 and related fixes from Fortnite

Change 3324295 on 2017/02/27 by Ben.Zeigler

	Add AssetManagerEditor which contains the editor interface for the AssetManager system, and engine code needed to support it
	Add support for Management references to the Reference Viewer, and add ability to extend that context menu from plugins/games
	Add struct customizations for PrimaryAssetId and PrimaryAssetType
	Add AssetAuditBrowser window that shows a specialized asset picker for auditing, accessible from content browser, reference viewer, and main windows pane
	Add AssetAuditContext, which is a cleaned up port of the one from Paragon. This needs some more work before being final
	Expose PropertyCustomizationHelpers::MakePropertyComboBox which allows making an "enum-like" combo box for struct customizations, it now works much like the PropertyEditorAsset UI
	Add Custom Column support to AssetView/AssetPicker. This can be used to show runtime-generated column data
	Fix bug in SAssetView where column view did not work with a filter predicate, because the column view was generated before the deferred filter predicate run, leading to an empty filter
	Port of CL #3323722 and related fixes from Fortnite

Change 3324398 on 2017/02/27 by Ben.Zeigler

	CIS fix

Change 3324442 on 2017/02/27 by Ben.Zeigler

	Nonunity fix discovered while testing my nonunity fix

Change 3325465 on 2017/02/28 by Marc.Audy

	Expand RawInput to support up to 20 buttons

Change 3325468 on 2017/02/28 by Marc.Audy

	Fix CIS

Change 3325887 on 2017/02/28 by Phillip.Kavan

	[UE-41893] Implicitly nativize child Blueprints that override one or more BlueprintCallable functions from a parent Blueprint.

	change summary:
	- added FBlueprintEditorUtils::ShouldNativizeImplicitly()
	- modified FBlueprintGlobalOptionsDetails::IsNativizeEnabled() to disable the 'Nativize' checkbox when the BP is implicitly enabled
	- modified FBlueprintGlobalOptionsDetails::GetNativizeState() to set the 'Checked' state when the BP is implicitly enabled
	- modified FBlueprintGlobalOptionsDetails::GetNativizeTooltip() to set an alternate tooltip for the disabled state (when the BP is implicitly enabled)
	- modified FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to ensure that implicitly-enabled BPs are flagged as selected for nativization

	#jira UE-41893

Change 3326713 on 2017/02/28 by Marc.Audy

	Update MAX_NUM_CONTROLLER_BUTTONS to match number of keys created

Change 3327688 on 2017/03/01 by Marc.Audy

	Fix spelling, remove autos

Change 3328139 on 2017/03/01 by Marc.Audy

	Win32 doesn't report the DeviceData in the same way that Win64 does, removing filtered check for now so that Win32 packaged games can use RawInput (4.15.1)

	#jira UE-42375

Change 3328550 on 2017/03/01 by Mike.Beach

	Typo fix in cast node tooltip.

Change 3328575 on 2017/03/01 by Nicholas.Blackford

	Submitting Tick Interval Functional Test

Change 3328972 on 2017/03/02 by Jack.Porter

	Fix for crash entering Landscape mode

	#jira UE-42497

Change 3329224 on 2017/03/02 by Nick.Bullard

	Removing Redirector from EngineTest project

Change 3330093 on 2017/03/02 by Mike.Beach

	Modified fix from Marc.Audy - Guarding against malformed graphs (missing their schema), which can happen in the middle of an undo transaction (removing the graph). Returning the graph's path name in this situation (instead of the display name), so we atleast have some semblance of context.

	#jira UE-42166

Change 3330306 on 2017/03/02 by Mike.Beach

	Replacing ArrayLibrary Get() calls in blueprints with a custom node, which can be toggled back and forth from returning by reference or by value.

	#jira UE-6451

Change 3330626 on 2017/03/02 by samuel.proctor

	Functional Test for Blueprint Containers

Change 3330690 on 2017/03/02 by Mike.Beach

	Modified the fix from CL 3308097 - cannot clear the edgraph pin context since many menu actions expect it be available still as the menu is clossing (menu's dismiss gets triggered before the action is executed).

	#jira UE-42500

Change 3330704 on 2017/03/02 by Mike.Beach

	CIS fix - fallout from CL 3330306

Change 3330875 on 2017/03/02 by Dan.Oconnor

	Iteration on compile manager - removed skeleton compile pass in favor of FastGenerateSkeletonClass (directly generate reflection data from blueprint source data - no graph cloning)

Change 3330892 on 2017/03/02 by Mike.Beach

	CIS fix for linux builds - include filename is case sensitive.

Change 3331585 on 2017/03/03 by Mike.Beach

	Fix for CIS issues (fallout from CL 3330306) - had success/failure return value flipped. Spuriously failing on deprecated node fixup.

Change 3333455 on 2017/03/06 by Ben.Zeigler

	Cleaned up version of CL #3332060, fixes crashes when calling StreamableManager::SynchronousLoad from inside a async PostLoad callback
	Also disable the "do sync load as async load" code in EDL, as EDL basically already does that internally
	Move the recursion guard inside async load tick outside of the EDL section, it's just as unsafe with EDL off

Change 3333484 on 2017/03/06 by Ben.Zeigler

	#jira UE-42312 Fix crash trying to read Searchable Name references to objects in the same package, now guess at package/object name

Change 3333553 on 2017/03/06 by Ben.Zeigler

	#jira UE-42387 Don't write out empty generated ini files for config files that are empty in both source and destination, this stops plugins without configs from ending up in cache

Change 3333697 on 2017/03/06 by Mike.Beach

	Resolving some CIS errors - fix for missed handling of split-struct pins (fallout from CL 3330306) on deprecated node conversion (mapping old pins to new pins).

Change 3334047 on 2017/03/06 by Ben.Zeigler

	#jira UE-42587 Now that we handle Add gameplay cues correctly by deferring them until after load, we also need to handle Remove cues, to avoid cues being stuck on permanently.

Change 3334228 on 2017/03/06 by Ben.Zeigler

	#jira UE-42153 Fix several crashes with gameplay tag query structs
	#jira UE-39760 Fix it to display tag query description on creation

Change 3335221 on 2017/03/07 by Lukasz.Furman

	fixed compilation errors for macros: UE_VLOG_MESH, UE_CVLOG_MESH
	#ue4

Change 3335733 on 2017/03/07 by dan.reynolds

	Fixing Attenuation Shape Material Reference

Change 3335918 on 2017/03/07 by Mike.Beach

	More deeply nesting an active world check in UMeshComponent::CacheMaterialParameterNameIndices(). Only guarding the parts that use the world (prior to this, we were blocking material parameter discovery, which was causing cooked content to loose material settings).

	#jira UE-42480

Change 3336053 on 2017/03/07 by zack.letters

	Moved and renamed test to meet naming convention and proper location

Change 3336087 on 2017/03/07 by Phillip.Kavan

	[UE-18618] Fix an ensure() misfire on PIE exit for listen server mode.

	change summary:
	- Modified UWorld::TransferBlueprintDebugReferences() to allow the LevelScript BP's target debug object reference to be reset to NULL when CreatePIEWorldBySavingToTemp() has recompiled it during the PIE startup process and autosaved the BP as a temporary.

	#jira UE-18618

Change 3336118 on 2017/03/07 by Phillip.Kavan

	Ensure that BP class component templates are included as preload dependencies where appropriate.

Change 3336418 on 2017/03/07 by Marc.Audy

	Set the PIEInstanceID before calling ConvertToPIEPackageName (4.15.1)
	#jira UE-42507

Change 3336529 on 2017/03/07 by dan.reynolds

	AEOverview UMG Interface

Change 3336729 on 2017/03/07 by Michael.Noland

	Blueprints: Changed a checkSlow() followed by unguarded access to an if and ensure() in BlueprintActionFilterImpl::IsDeprecated to prevent a potential crash in release if the node class is invalid for some reason
	#jira UE-42519

Change 3337054 on 2017/03/08 by Mieszko.Zielinski

	Fixed UGameplayTaskResource::AutoResourceID getting cleared on hot reload #UE4

Change 3337605 on 2017/03/08 by Mieszko.Zielinski

	PR #3345: Fix reversed comparison in FGameplayResourceSet::HasAllIDs (Contributed by hoelzl)

Change 3337612 on 2017/03/08 by Lina.Halper

	Commenting out ensure as this doesn't cause any harm and fix it up later by itself.
	- adding ticket for further investigation

	#rb: Martin.Wilson
	#jira: UE-42062

Change 3338353 on 2017/03/08 by Mike.Beach

	Undoing CL 3320099, and instead allowing self nodes to be plugged into const ref inputs. Now auto-generating ref terms for the self node (the input param expects an addressable UProperty). Skipping this for native functions, as UHT already does something similar.

	#jira UE-40861

Change 3340052 on 2017/03/09 by Marc.Audy

	Don't mark a blueprint dirty if the default value isn't actually set
	#jira UE-42511

Change 3340211 on 2017/03/09 by samuel.proctor

	Adding TMap/TSet tests for Containers Functional Test

Change 3340272 on 2017/03/09 by Marc.Audy

	auto removals
	small optimizations

Change 3340341 on 2017/03/09 by Marc.Audy

	Fortnite fixes for blueprint exposed editor only struct members
	#jira UE-42430

Change 3340356 on 2017/03/09 by Marc.Audy

	Do not allow blueprint exposed editor only struct members
	#jira UE-42430

Change 3340369 on 2017/03/09 by Mike.Beach

	Certain operations expect set/map elements to be constructed, instead of using an 'uninitialized' value (like with FStrings, previously this would blow up attempting to assign a value to an FString that hadn't been constructed). Fix is to construct the member when we make space in the container (emulating execSetArray).

	#jira UE-42572

Change 3340445 on 2017/03/09 by mason.seay

	Renamed and updated test map.  Also disabled tests until reviewed

Change 3340627 on 2017/03/09 by Marc.Audy

	Remove autos

Change 3340639 on 2017/03/09 by Dan.Oconnor

	Avoid CDO creation when asking if an object IsDefaultSubobject

Change 3340642 on 2017/03/09 by Marc.Audy

	Correctly maintain removed items from arrays when duplicating actors via T3D
	#jira UE-42278

Change 3340689 on 2017/03/09 by Dan.Oconnor

	Avoid UObject::Modify calls when renaming edgraph nodes as part of UEdGraphNode::PostLoad() or UEdGraph::MoveNodesToAnotherGraph

Change 3340709 on 2017/03/09 by Dan.Oconnor

	Remove misplace dClassDefaultObject null check for now

Change 3340710 on 2017/03/09 by Dan.Oconnor

	Avoid FindRedirectedPropertyName when performing StaticDuplicateObject

Change 3340728 on 2017/03/09 by Dan.Oconnor

	Null checking CDO so that we can duplicate a class with no CDO

Change 3342184 on 2017/03/10 by mason.seay

	Nav mesh generation test - not finished

Change 3342930 on 2017/03/13 by Mieszko.Zielinski

	Added missing undefining of local macros in VisualLoggerAutomationTests.cpp #UE4

Change 3343739 on 2017/03/13 by Marc.Audy

	Protect against ChildActorClass becoming null while ChildActorTemplate remains valid.

Change 3343758 on 2017/03/13 by Marc.Audy

	Ensure that when you change visibility, children also get marked dirty as needed.
	SetVisibility is no longer virtual, use OnVisibilityChanged in subclasses instead
	#jira UE-42240

Change 3343816 on 2017/03/13 by Mike.Beach

	Making sure we build CrashReporter for nativized clients.

	#jira UE-42056

Change 3343858 on 2017/03/13 by Phillip.Kavan

	Back out changelist 3336118 (per discussion) - did not solve the issue.

Change 3344218 on 2017/03/13 by Mike.Beach

	Patching some holes in the wildcard pin logic for our new array GetItem node (making sure the node properly retains its type).

Change 3344388 on 2017/03/13 by Mike.Beach

	Preventing make/break nodes from being in the context menu for structs that are not labeled 'BlueprintType' (still available if you drag off a node with a struct pin of that type).

	#jira UE-37971

Change 3344411 on 2017/03/13 by dan.reynolds

	AEOverviewMain update

	- Organized Variables

	- Added comments on level interface with UI script

Change 3344956 on 2017/03/14 by Marc.Audy

	Remove autos
	Slight optimization

Change 3345365 on 2017/03/14 by Mike.Beach

	In the Blueprint diff tool, no longer assuming that graph names are unique (using the outer path to find matching graphs between diff panels).

	#jira UE-42787

Change 3345565 on 2017/03/14 by Marc.Audy

	auto removal

Change 3345654 on 2017/03/14 by Marc.Audy

	Allow hierarchical metadata querying when HACK_HEADER_GENERATION is true

Change 3345771 on 2017/03/14 by Zak.Middleton

	#ue4 - Refactored CharacterMovementComponent determination of net send rate when combining moves into a virtual function GetClientNetSendDeltaTime(). Added configurable values to GameNetworkManager under [/Script/Engine.GameNetworkManager]:

	ClientNetSendMoveDeltaTime=0.0111f
	ClientNetSendMoveDeltaTime=0.0222f
	ClientNetSendMoveThrottleAtNetSpeed = 10000
	ClientNetSendMoveThrottleOverPlayerCount=10

	These are the default values maintained for backwards compat.

	Related to OR-36422.

Change 3346314 on 2017/03/14 by Dan.Oconnor

	Add two features to FBlueprintCompileReinstancer. Exposing it's CPFUO extensions and add a flag to avoid potentially unneeded CDO duplication.

Change 3346329 on 2017/03/14 by Dan.Oconnor

	Avoid CDO creation in UBlueprintGeneratedClass::PostLoad - rely instead on compiler

Change 3346436 on 2017/03/14 by Dan.Oconnor

	Compilation Manager iteration - improvements to reinstancing logic and postponement of reinstancing reference replacement until after loading has finished (done strictly to reduce the number of 'find references' calls). Behavior change is behind the GMinimalCompileOnLoad flag

Change 3346632 on 2017/03/14 by Ben.Zeigler

	Change StringClassReference customization to use MustImplement and BlueprintBaseOnly metadata, to match the metadata used by SubclassOf customization
	Add missing Class Property metadata to the metadata list

Change 3347525 on 2017/03/15 by Marc.Audy

	PR #3371: Fix for binding ability action to input component (Contributed by ryanjon2040)
	#jira UE-42810

Change 3347562 on 2017/03/15 by Phillip.Kavan

	[UE-32816] Support for value-based bitfield enum associations in the editor.

	notes:
	- default mode is still index-based, so there are no backwards-compatibility issues

	change summary:
	- new metadata key for flagging enums as value-based (UseEnumValuesAsMaskValuesInEditor)
	- modified SPropertyEditorNumeric::Construct() to include logic for handling value-based enum associations
	- modified SGraphPinInteger::Construct() to include logic for handling value-based enum associations
	- added default value fixup to UK2Node_BitmaskLiteral, so that changed/removed values get masked out on load
	- switched UK2Node_BitmaskLiteral::PostLoad() to Serialize(), so that default value fixup occurs before compilation

	#jira UE-32816

Change 3348030 on 2017/03/15 by Marc.Audy

	Remove experimental blueprintable components setting, they are supported fully

Change 3348034 on 2017/03/15 by Phillip.Kavan

	CIS fix.

Change 3348054 on 2017/03/15 by Marc.Audy

	Fix shadow error

Change 3348063 on 2017/03/15 by mason.seay

	Updateed bp logic to use asserts.  Added scenarios to descriptions of tests

Change 3348131 on 2017/03/15 by mason.seay

	Updating maps and reorganizing content

Change 3348146 on 2017/03/15 by Mike.Beach

	Making it so we can use DataTable variables as inputs in the GetDataTableRow node. The output pin is now a wildcard when the row type is undefined, and we throw an access error at runtime if the table and output type don't match.

Change 3348213 on 2017/03/15 by dan.reynolds

	AEOverview UMG Update

	- Added level selection persistence between categories (so you can pick and choose from multiple categories)

	- Added a clear all selections button

	- Added comments to the UMG BP

Change 3348344 on 2017/03/15 by Lukasz.Furman

	fixed missing path following result flag descriptions
	#ue4

Change 3348489 on 2017/03/15 by mason.seay

	Moved content and updated test descriptions

Change 3348496 on 2017/03/15 by Mike.Beach

	Keeping the new version of the GetArrayItem node from causing a stack overflow with wildcard reroute nodes.

Change 3348502 on 2017/03/15 by Ben.Zeigler

	#jira UE-42935 Fix several issues with GameplayTag and Container switch nodes crashing. Container didn't handling having multiple empty nodes correctly
	Fix general issue with Switch nodes where removing an execution pin with right click was not synchronizing the pin list properly
	Change it so the Container switch shows the simple tag string instead of Case 0, and change it to not quote by default for Container display strings

Change 3348504 on 2017/03/15 by Ben.Zeigler

	#jira UE-41554 Add GameplayTag initialization to InitializeObjectReferences if it hasn't been initialized yet, this is important so it gets initialized before being initialized from unsafe areas like Serialize

Change 3348512 on 2017/03/15 by Mike.Beach

	Reroute nodes connected to a new output, will propagate the type through its inputs (was previously treating the input's wildcard type as authoritative).

Change 3348513 on 2017/03/15 by Phillip.Kavan

	[UE-38979] Error out on an attempt to nativize a Blueprint class that also implements a native C++ interface with a pure virtual function declaration.

	change summary:
	- added TIsAbstract<T> for traits testing to see if native C++ types are abstract (in terms of C++, not UE4)
	- changed TCppStructOps::IsAbstract() to use TIsAbstract<T>
	- added UClass::CppClassOps to capture class-specific traits info for the underlying C++ class type
	- modified UClass::PurgeClass() to clean up class-specific traits info (if valid)
	- modified FNativeClassHeaderGenerator::ExportNativeGeneratedInitCode() to generate code to initialize class-specific traits info for compiled-in class types
	- modified FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to throw an error during nativization if a target BP class is found to implement a native interface class that's also abstract (i.e. an interface class that declares one or more of its methods as pure virtual)
	- modified BlueprintActionFilterImpl::IsExtraneousInterfaceCall() to initially exclude any native interface class that is also abstract
	- modified FKismetEditorUtilities::CanBlueprintImplementInterface() to additionally exclude any native class that is also abstract
	- modified FBlueprintInterfaceFilter::IsClassAllowed() to additionally exclude any native class that is also abstract

	#jira UE-38979

Change 3348651 on 2017/03/15 by Mike.Beach

	Fixing the new GetDataTableRow node so that it'll give you the option of reroute nodes.

Change 3348684 on 2017/03/15 by Michael.Noland

	Blueprints: Allow string and text variables to be marked as multi-line

	PR #3294: UE-42147: Add multiline to BP view details (Contributed by projectgheist)

	#jira UE-42275

Change 3348691 on 2017/03/15 by Michael.Noland

	Cameras: Added support for specifying a default aspect ratio and whether or not to constrain to it in a camera manager subclass; useful when using custom view logic that doesn't source from a camera component as the view target
	PR #2593: Finish implementing aspect ratio handling for PlayerCameraManager (Contributed by CleanCut)
	#jira UE-33052

Change 3348698 on 2017/03/15 by Michael.Noland

	Removed a sprite reference from trigger shape classes and excluded some component references from camera rigs in cooked builds
	PR #2922: Ensuring editor data is not accessed when excluded from cook (Contributed by moritz-wundke)

	#jira UE-38484

Change 3348722 on 2017/03/15 by Dan.Oconnor

	Fix replacement bug - due to last minute refactor of this reference replacer call

Change 3348736 on 2017/03/15 by Michael.Noland

	Blueprints: Added missing include for UTextProperty (compiled fine locally both with the file checked out and the file unmodified)

Change 3348810 on 2017/03/15 by Michael.Noland

	Blueprints: Added support for seeing the user defined tooltip on get/set nodes for local variables
	PR #3256: UE-41098: Added UFunction argument (Contributed by projectgheist)

Change 3348811 on 2017/03/15 by Michael.Noland

	PR #3380: Added CancelAbility Blueprint node (Contributed by ryanjon2040)

	#jira UE-42904

Change 3348969 on 2017/03/15 by Dan.Oconnor

	Build fix

Change 3349023 on 2017/03/16 by Aaron.McLeran

	Copying //Tasks/UE4/Private-GDC17-Audio to Dev-Framework (//UE4/Dev-Framework)

Change 3349389 on 2017/03/16 by mason.seay

	Finished up Navigation map.  Improved Navmesh map (still needs some work before review)

Change 3349575 on 2017/03/16 by Marc.Audy

	Emit ScriptMacros.h in addition to ObjectMacros.h in generated headers

Change 3349628 on 2017/03/16 by Ben.Zeigler

	Add direct support for Chunk setting to AssetManager. If AssetManager exists and no game callback is set it uses the new, much faster method. Otherwise it falls back to the old one
	Fix some memory corruption issues in ChunkManifestGenerator where it was modifying a map while iterating it, could lead to assets ending up in multiple chunks accidentally
	Remove the "Old Cooker" entirely, it hasn't functioned since around 4.9
	Various fixes to AssetManagerEditorModule
	Convert ShooterGame to use the AssetManager for chunking

Change 3349629 on 2017/03/16 by Ben.Zeigler

	Change Fortnite to use the AssetManager chunking system, which simplifies the chunk 1 setup significantly
	Also includes changes made on Fortnite Branch as  CL #3323724:
	Fortnite changes to take advantage of the Manage dependency in the asset manager
	Move definition of asset types to ini from native, and simplify it so all zone themes are scanned, even if not used
	Make FeedbackBank a primary asset type. It's currently editor only as there are some outdated banks we don't want to cook

Change 3350043 on 2017/03/16 by Marc.Audy

	Fix Audio compile errors

Change 3350092 on 2017/03/16 by Dan.Oconnor

	Fix missing output parameters when the function result node is pruned

Change 3350190 on 2017/03/16 by Ben.Zeigler

	CIS fix

Change 3350707 on 2017/03/16 by Dan.Oconnor

	Add means of enabling BlueprintCompilationManager via editor ini. Wedging the check into LaunchEngineLoop because of assets that are loaded during engine initialization

Change 3350820 on 2017/03/16 by Joe.Conley

	Xenakis project: Setting GameMode to GameMode instead of None so the game doesn't crash on Play

Change 3350893 on 2017/03/16 by Dan.Oconnor

	Build fix

Change 3351017 on 2017/03/16 by Dan.Oconnor

	Using ordered arguments instead of named arguments improves load time in BP heavy projects

Change 3351056 on 2017/03/16 by Dan.Oconnor

	Avoiding Copies

Change 3351062 on 2017/03/16 by Dan.Oconnor

	Enable BlueprintCompilationManager by default - this is a major change in code path when loading uassets that contain blueprints

Change 3351770 on 2017/03/17 by Marc.Audy

	Fix CIS warnings

Change 3351818 on 2017/03/17 by Mike.Beach

	CopyPropertiesForUnrelatedObjects() will now only copy tagged data when the two objects truly are unrelated (different native base classes). We have to do this because the two native base classes may have different serialization methods that add/expect different data, which is not compatible with the other.

	#jira UE-35970

Change 3351918 on 2017/03/17 by Mike.Beach

	CIS fix - renaming local so it doesn't conflict with the one in the outer scope.

Change 3351931 on 2017/03/17 by Ben.Zeigler

	Make CoreRedirects a proper Automated Test, and fix a test failure with not handling : in the output string
	Fix legitimate regression where doing a package -> package rename would clear Outer, this was a result of a fix I made in Main a few weeks ago

Change 3351956 on 2017/03/17 by Dan.Oconnor

	Make sure result element is emptied when calling Intersect, Union, or Difference
	#jira UE-42993

Change 3352049 on 2017/03/17 by Ben.Zeigler

	#Jira UE-42118 Add RemoveGameplayTag to the tag blueprint library
	Delete (with redirector) redundant AddGameplayTagToContainer function that got accidentally added in parallel on Fortnite. Decided to keep the shorter TagContainer parameter name for both though

Change 3352065 on 2017/03/17 by Aaron.McLeran

	Fixing compile errors

	- deleting unused files
	- removing #pragma once in SSynthKnob.cpp
	- Making phonon have win64 whitelist to avoid compiling on other platforms

Change 3352100 on 2017/03/17 by Aaron.McLeran

	Fixing compile errors

	- Moving header file to public folder since it's used outside of module

Change 3352182 on 2017/03/17 by Ben.Zeigler

	#jira UE-39815 Fix several issues with renaming tags in the tag settings view, it now deletes redirectors properly when renaming or making a new tag that matches an existing redirector

Change 3352286 on 2017/03/17 by Ben.Zeigler

	#jira UE-39519 Add error messages when only one of GameMode/GameState is derived from the outdated parent classes
	Modified version of PR #3285: Add error log messages if the GameMode/GameState are mis-matched (Contributed by jwatte)

Change 3352299 on 2017/03/17 by Ben.Zeigler

	#jira UE-40544
	PR #3130: UE-40544: Check pause state if state change is allowed (Contributed by projectgheist)

Change 3352303 on 2017/03/17 by Ben.Zeigler

	#jira UE-40856
	Commit PR #3147:  Remove unnecessary directory separator for GetSaveGamePath  (Contributed by projectgheist)
	Remove unnecessary FString casting and in OpenGLDebugFrameDump.cpp there were FString multiplications that would never compile

Change 3352320 on 2017/03/17 by Ben.Zeigler

	#jira UE-40087 Fix it so console keybind can be used in shipping games with console enabled
	Commit PR #3079: Fix ALLOW_CONSOLE define usage (Contributed by KrisRedbeard)

Change 3352338 on 2017/03/17 by Ben.Zeigler

	#jira UE-42800
	PR #3367: Made CheatManager more useful for non-FPShooters (Contributed by crumblycake)

Change 3352352 on 2017/03/17 by Dan.Oconnor

	Emptying map instead of trying to remove an element when conversion of a value type fails - can't remove a single element until the map is rehashed
	#jira UE-42937

Change 3352581 on 2017/03/17 by Lukasz.Furman

	fixed memory leak in navmesh generators
	copy of CL# 3352356
	#ue4

Change 3352665 on 2017/03/17 by Aaron.McLeran

	Fixing build error

	- Adding virtual destructor to FSoundWaveSoundWaveAssetActionExtender
	- Also renamed the class to only include SoundWave once!
	- Fixing static analysis warning on null deref.

Change 3352685 on 2017/03/17 by Dan.Oconnor

	Fix for bad behavior of GetValues and GetKeys functions when there are gaps in a TMap (e.g. due to Remove calls)
	#jira UE-42547

Change 3352706 on 2017/03/17 by Aaron.McLeran

	Fixing build error

	Changing TSharedPtr<FSoundWaveSoundWaveAssetActionExtender> to TSharedPtr<ISoundWaveAssetActionExtensions>

Change 3352708 on 2017/03/17 by Dan.Oconnor

	Data only and interface blueprints need SkeletonGeneratedClass set on load so that they can be used by the BlueprintEditor

	#jira UE-43023

Change 3352860 on 2017/03/17 by Lukasz.Furman

	fixed memory leak in navmesh generators
	copy of CL# 3352849
	#ue4

Change 3352967 on 2017/03/17 by Dan.Oconnor

	Avoid tagging blueprints as modified while compiling with the new compilation manager. Leaving old code path unaffected, although it may benefit from this change.

	#jira UE-43027

Change 3352979 on 2017/03/17 by Dan.Oconnor

	Static analysis driven fixes

	#jira UE-43044

Change 3352987 on 2017/03/17 by Aaron.McLeran

	Fixing build error

	- Removing myo from other platforms, win64 only

Change 3353234 on 2017/03/18 by Marc.Audy

	Fix Win32 build

Change 3353344 on 2017/03/19 by Marc.Audy

	Fix cyclic includes in new Audio code

Change 3353350 on 2017/03/19 by Marc.Audy

	Disable static analysis for myo third party code

Change 3353750 on 2017/03/20 by Marc.Audy

	Fix additional cyclic include

Change 3353926 on 2017/03/20 by Mieszko.Zielinski

	Made FNavAgentProperties::GetExtent return INVALID_NAVEXTENT if prop's AgentRadius is not set #UE4

	This results in using FNavAgentProperties::DefaultProperties in navigation system queries to fallback to default query extent.

	#jira UE-18493

Change 3354249 on 2017/03/20 by Mike.Beach

	Raising a UHT error if you use a non-byte enum type in a Blueprint function. Blueprints currently only support uint8 enums (already an error if you tag the enum with 'BlueprintType', this error just emulates/extends that one).

	#jira UE-42479

Change 3354464 on 2017/03/20 by Dan.Oconnor

	Fix missing source path when using compilation manager

Change 3354499 on 2017/03/20 by Dan.Oconnor

	Disable compilation manager

Change 3354620 on 2017/03/20 by Ben.Zeigler

	#jira UE-43087 Fix crash when calling HasGPUEmitter on a Server build, this is newly an issue because it is calling GetAssetRegistryTags in more places than it used to

Change 3354714 on 2017/03/20 by Michael.Noland

	PR #3352: Fixed issue with diffed Blueprints being searchable (Contributed by MichaelSchoell)

	#jira UE-42655

Change 3354718 on 2017/03/20 by Michael.Noland

	Engine: Change FViewport::IsGameRenderingEnabled to be static
	PR #3317: FViewport::IsGameRenderingEnabled (Contributed by tomix1024)

	#jira UE-42471

Change 3354721 on 2017/03/20 by Michael.Noland

	PR #3293: Made GetDefaultLocale accessible in blueprint (Contributed by derekvanvliet)

	#jira UE-42274

Change 3354907 on 2017/03/20 by Aaron.McLeran

	Fixing content in xenakis map

Change 3355223 on 2017/03/20 by Ben.Zeigler

	#jira UE-43096 Fix crash when trying to ResolveName a path that ends in . (apparently when you LoadObject empty string, it ends up trying to load "." before giving up

Change 3355297 on 2017/03/20 by Dan.Oconnor

	Fix incorrect flag settings from fast skeleton path.. this is part of the fix for UE-43083

Change 3355373 on 2017/03/20 by Michael.Noland

	PR #3222: Allow Blueprint Variables to be Readonly (Contributed by FrostByteGER)

	#jira UE-41640

Change 3355417 on 2017/03/20 by Ben.Zeigler

	Fix formatting bug where I forgot some braces

Change 3355462 on 2017/03/20 by Aaron.McLeran

	UE-43046 Property type changed with no possible conversion

	Resaved asset in question

Change 3355629 on 2017/03/20 by Dan.Oconnor

	Don't warn the user when their return node that has no pins (other than an exec pin). These return nodes cannot be deleted and connecting them does nothing. Prior to recent changes the warning never fired because the return node would be pruned and not validated.

Change 3355631 on 2017/03/20 by Dan.Oconnor

	Fix compilation results spam in compilation manager. Scoped compiler events (e.g. BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileTime);) will flush the results log if no 'event' has been started. Timing data collected via this mechanism will not be useful (can only measure entire call to ::Flush in compilation manager)

Change 3356127 on 2017/03/21 by Richard.Hinckley

	#jira UEDOC-4711
	Updated an invalid/old URL in a comment to a valid/current URL.

Change 3356193 on 2017/03/21 by Marc.Audy

	Temporarily remove editor only properties in FCameraFocusSettings until we correctly no longer create pins for struct properties that are not exposed to blueprints
	#jira UE-43420

Change 3356222 on 2017/03/21 by Marc.Audy

	Expose new attenuation settings to blueprints to resolve cook warnings.

Change 3356286 on 2017/03/21 by Richard.Hinckley

	#jira UEDOC-4711
	Selected a different URL for the update.

Change 3356339 on 2017/03/21 by Marc.Audy

	Delete unconnected return nodes to fix fortnite cook warnings

Change 3356827 on 2017/03/21 by Ben.Zeigler

	Explicitly disable copy operations for streamable manager objects. This may be causing some obscure crashes like WEX-5182 but I am not sure how the copy constructor would be getting called. Either way it's unsafe
	Put in protection against passing in duplicate items to RequestAsyncLoad, which is another possible cause of internal data corruption
	Add some more ensures to track down possible issues with handle corruption

Change 3356920 on 2017/03/21 by Ben.Zeigler

	Fix ensure just checked in to not go off when handles are halfway through being cancelled

Change 3358152 on 2017/03/22 by Phillip.Kavan

	#jira UE-43102 - Fix an occasional crash on load in nativized EDL-enabled builds with non-nativized child BPs.

	Change summary:
	- Modified AActor::PostLoadSubobjects() to skip the CheckAndApplyComponentTemplateOverrides() call in the CDO case; at that point the ICH may not be fully loaded, but we don't require the non-nativized child BP's CDO to be fixed up anyway.

[CL 3358685 by Marc Audy in Main branch]
2017-03-22 12:57:30 -04:00

5597 lines
194 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
OpenGLDebugFrameDump.cpp: Implementation of debug code that allows creating detailed summaries of OpenGL pipeline state for all draws in a single frame.
=============================================================================*/
#include "CoreMinimal.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/OutputDeviceFile.h"
#include "Modules/ModuleManager.h"
#include "OpenGLDrv.h"
/** This define decreases file size, but substantially increases frame dump time. Also, if you turn it off, make sure your image viewer correctly displays BMP files with alpha. */
#define USE_COMPRESSED_PNG_INSTEAD_OF_BMP_FOR_CONTENT_OUTPUT 1
#if USE_COMPRESSED_PNG_INSTEAD_OF_BMP_FOR_CONTENT_OUTPUT
// For PNG compression
#include "Interfaces/IImageWrapperModule.h"
const GLenum TextureOutputFormat = GL_RGBA;
#else
const GLenum TextureOutputFormat = GL_BGRA;
#endif
#define DEBUG_GL_ERRORS_CAUSED_BY_THIS_CODE 1
#if DEBUG_GL_ERRORS_CAUSED_BY_THIS_CODE
#define ASSERT_NO_GL_ERROR() check( glGetError() == GL_NO_ERROR )
#else
#define ASSERT_NO_GL_ERROR()
#endif
extern bool GDisableOpenGLDebugOutput;
#if USE_COMPRESSED_PNG_INSTEAD_OF_BMP_FOR_CONTENT_OUTPUT
void appCreatePNGWithAlpha( const TCHAR* File, int32 Width, int32 Height, FColor* Data, IFileManager* FileManager = &IFileManager::Get() )
{
// We assume all resources are png for now.
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>( FName("ImageWrapper") );
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG );
if ( ImageWrapper.IsValid() && ImageWrapper->SetRaw( Data, 4 * Width * Height, Width, Height, ERGBFormat::RGBA, 8 ) )
{
FArchive* Ar = FileManager->CreateFileWriter( File );
if( !Ar )
return;
const TArray<uint8>& CompressedData = ImageWrapper->GetCompressed();
int32 CompressedSize = CompressedData.Num();
Ar->Serialize( (void*)CompressedData.GetData(), CompressedSize );
delete Ar;
}
}
#else
void appCreateBitmapWithAlpha( const TCHAR* File, int32 Width, int32 Height, FColor* Data, IFileManager* FileManager = &IFileManager::Get() )
{
FArchive* Ar = FileManager->CreateFileWriter( File );
if( !Ar )
return;
// Types.
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack (push,1)
#endif
struct BITMAPFILEHEADER
{
uint16 bfType GCC_PACK(1);
uint32 bfSize GCC_PACK(1);
uint16 bfReserved1 GCC_PACK(1);
uint16 bfReserved2 GCC_PACK(1);
uint32 bfOffBits GCC_PACK(1);
} FH;
struct BITMAPINFOHEADER
{
uint32 biSize GCC_PACK(1);
int32 biWidth GCC_PACK(1);
int32 biHeight GCC_PACK(1);
uint16 biPlanes GCC_PACK(1);
uint16 biBitCount GCC_PACK(1);
uint32 biCompression GCC_PACK(1);
uint32 biSizeImage GCC_PACK(1);
int32 biXPelsPerMeter GCC_PACK(1);
int32 biYPelsPerMeter GCC_PACK(1);
uint32 biClrUsed GCC_PACK(1);
uint32 biClrImportant GCC_PACK(1);
} IH;
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack (pop)
#endif
uint32 BytesPerLine = Width * 4;
// File header.
FH.bfType = INTEL_ORDER16((uint16) ('B' + 256*'M'));
FH.bfSize = INTEL_ORDER32((uint32) (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + BytesPerLine * Height));
FH.bfReserved1 = INTEL_ORDER16((uint16) 0);
FH.bfReserved2 = INTEL_ORDER16((uint16) 0);
FH.bfOffBits = INTEL_ORDER32((uint32) (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)));
Ar->Serialize( &FH, sizeof(FH) );
// Info header.
IH.biSize = INTEL_ORDER32((uint32) sizeof(BITMAPINFOHEADER));
IH.biWidth = INTEL_ORDER32((uint32) Width);
IH.biHeight = INTEL_ORDER32((uint32) Height);
IH.biPlanes = INTEL_ORDER16((uint16) 1);
IH.biBitCount = INTEL_ORDER16((uint16) 32);
IH.biCompression = INTEL_ORDER32((uint32) 0); //BI_RGBA
IH.biSizeImage = INTEL_ORDER32((uint32) BytesPerLine * Height);
IH.biXPelsPerMeter = INTEL_ORDER32((uint32) 0);
IH.biYPelsPerMeter = INTEL_ORDER32((uint32) 0);
IH.biClrUsed = INTEL_ORDER32((uint32) 0);
IH.biClrImportant = INTEL_ORDER32((uint32) 0);
Ar->Serialize( &IH, sizeof(IH) );
// Colors.
for( int32 i=Height-1; i>=0; i-- )
{
for( int32 j=0; j<Width; j++ )
{
Ar->Serialize( &Data[i*Width+j].B, 1 );
Ar->Serialize( &Data[i*Width+j].G, 1 );
Ar->Serialize( &Data[i*Width+j].R, 1 );
Ar->Serialize( &Data[i*Width+j].A, 1 );
}
}
// Success.
delete Ar;
}
#endif
// A simple code to save a single surface into separate DDS file.
// I know there's a TextureFormatDXT module, but it's too intertwined with the rest of the engine,
// and I prefer this small function to be kept independent of everything, as it's only for one simple debug job, and
// to not introduce a lot of UE4 types like FImage that aren't really necessary.
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3)\
((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) |\
((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 ))
#endif
void appCreateDDSWithSingleSurface( const TCHAR* File, int32 Width, int32 Height, GLint InternalFormat, const void* Data, uint32 DataSize, IFileManager* FileManager = &IFileManager::Get() )
{
FArchive* Ar = FileManager->CreateFileWriter( File );
if( !Ar )
return;
// Types.
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack (push,1)
#endif
struct DDSPixelFormat
{
uint32 Size GCC_PACK(1);
uint32 Flags GCC_PACK(1);
uint32 FourCC GCC_PACK(1);
uint32 RGBBitCount GCC_PACK(1);
uint32 RBitMask GCC_PACK(1);
uint32 GBitMask GCC_PACK(1);
uint32 BBitMask GCC_PACK(1);
uint32 ABitMask GCC_PACK(1);
};
struct DDSHeader
{
uint32 Size GCC_PACK(1);
uint32 Flags GCC_PACK(1);
uint32 Height GCC_PACK(1);
uint32 Width GCC_PACK(1);
uint32 PitchOrLinearSize GCC_PACK(1);
uint32 Depth GCC_PACK(1);
uint32 MipMapCount GCC_PACK(1);
uint32 Reserved[11] GCC_PACK(1);
struct DDSPixelFormat PixelFormat;
uint32 Caps1 GCC_PACK(1);
uint32 Caps2 GCC_PACK(1);
uint32 Reserved2[3] GCC_PACK(1);
} Header;
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack (pop)
#endif
uint8 FileType[] = "DDS ";
Ar->Serialize( FileType, sizeof(uint8)*4 );
check( sizeof(Header) == 124 );
memset( &Header, 0, sizeof(Header) );
Header.Size = INTEL_ORDER32((uint32) 124);
Header.Flags = INTEL_ORDER32((uint32) 0x81007);
Header.Width = INTEL_ORDER32((uint32) Width);
Header.Height = INTEL_ORDER32((uint32) Height);
Header.PitchOrLinearSize = INTEL_ORDER32((uint32) DataSize);
Header.Depth = INTEL_ORDER32((uint32) 1);
Header.MipMapCount = INTEL_ORDER32((uint32) 1);
Header.Caps1 = INTEL_ORDER32((uint32) 0x1000);
Header.PixelFormat.Size = INTEL_ORDER32((uint32) 32);
Header.PixelFormat.Flags = INTEL_ORDER32((uint32) 4);
switch( InternalFormat )
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('D','X','T','1') );
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('D','X','T','3') );
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('D','X','T','5') );
break;
case GL_COMPRESSED_RED_RGTC1:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('B','C','4','U') ); // BC4 UNORM
break;
case GL_COMPRESSED_SIGNED_RED_RGTC1:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('B','C','4','S') ); // BC4 SNORM
break;
case GL_COMPRESSED_RG_RGTC2:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('A','T','I','2') ); // BC5 UNORM
break;
case GL_COMPRESSED_SIGNED_RG_RGTC2:
Header.PixelFormat.FourCC = INTEL_ORDER32((uint32) MAKEFOURCC('B','C','5','S') ); // BC5 SNORM
break;
default:
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unknown internal format ( 0x%x ) while saving DDS file '%s'. Resulting DDS may be unreadable."), InternalFormat, *File );
break;
}
Ar->Serialize( &Header, sizeof(Header) );
Ar->Serialize( (void*)Data, DataSize );
delete Ar;
}
//-----------------------------------------------------------------------------
#if ENABLE_OPENGL_FRAMEDUMP || ENABLE_UNIFORM_BUFFER_LAYOUT_DUMP
const TCHAR* GetGLUniformTypeString( GLint UniformType )
{
switch( UniformType )
{
case GL_FLOAT: return TEXT("GL_FLOAT");
case GL_FLOAT_VEC2: return TEXT("GL_FLOAT_VEC2");
case GL_FLOAT_VEC3: return TEXT("GL_FLOAT_VEC3");
case GL_FLOAT_VEC4: return TEXT("GL_FLOAT_VEC4");
case GL_INT: return TEXT("GL_INT");
case GL_INT_VEC2: return TEXT("GL_INT_VEC2");
case GL_INT_VEC3: return TEXT("GL_INT_VEC3");
case GL_INT_VEC4: return TEXT("GL_INT_VEC4");
case GL_UNSIGNED_INT: return TEXT("GL_UNSIGNED_INT");
case GL_UNSIGNED_INT_VEC2: return TEXT("GL_UNSIGNED_INT_VEC2");
case GL_UNSIGNED_INT_VEC3: return TEXT("GL_UNSIGNED_INT_VEC3");
case GL_UNSIGNED_INT_VEC4: return TEXT("GL_UNSIGNED_INT_VEC4");
case GL_BOOL: return TEXT("GL_BOOL");
case GL_BOOL_VEC2: return TEXT("GL_BOOL_VEC2");
case GL_BOOL_VEC3: return TEXT("GL_BOOL_VEC3");
case GL_BOOL_VEC4: return TEXT("GL_BOOL_VEC4");
case GL_FLOAT_MAT2: return TEXT("GL_FLOAT_MAT2");
case GL_FLOAT_MAT3: return TEXT("GL_FLOAT_MAT3");
case GL_FLOAT_MAT4: return TEXT("GL_FLOAT_MAT4");
case GL_FLOAT_MAT2x3: return TEXT("GL_FLOAT_MAT2x3");
case GL_FLOAT_MAT2x4: return TEXT("GL_FLOAT_MAT2x4");
case GL_FLOAT_MAT3x2: return TEXT("GL_FLOAT_MAT3x2");
case GL_FLOAT_MAT3x4: return TEXT("GL_FLOAT_MAT3x4");
case GL_FLOAT_MAT4x2: return TEXT("GL_FLOAT_MAT4x2");
case GL_FLOAT_MAT4x3: return TEXT("GL_FLOAT_MAT4x3");
case GL_SAMPLER_1D: return TEXT("GL_SAMPLER_1D");
case GL_SAMPLER_2D: return TEXT("GL_SAMPLER_2D");
case GL_SAMPLER_3D: return TEXT("GL_SAMPLER_3D");
case GL_SAMPLER_CUBE: return TEXT("GL_SAMPLER_CUBE");
case GL_SAMPLER_1D_SHADOW: return TEXT("GL_SAMPLER_1D_SHADOW");
case GL_SAMPLER_2D_SHADOW: return TEXT("GL_SAMPLER_2D_SHADOW");
case GL_SAMPLER_1D_ARRAY: return TEXT("GL_SAMPLER_1D_ARRAY");
case GL_SAMPLER_2D_ARRAY: return TEXT("GL_SAMPLER_2D_ARRAY");
case GL_SAMPLER_1D_ARRAY_SHADOW: return TEXT("GL_SAMPLER_1D_ARRAY_SHADOW");
case GL_SAMPLER_2D_ARRAY_SHADOW: return TEXT("GL_SAMPLER_2D_ARRAY_SHADOW");
case GL_SAMPLER_2D_MULTISAMPLE: return TEXT("GL_SAMPLER_2D_MULTISAMPLE");
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return TEXT("GL_SAMPLER_2D_MULTISAMPLE_ARRAY");
case GL_SAMPLER_CUBE_SHADOW: return TEXT("GL_SAMPLER_CUBE_SHADOW");
case GL_SAMPLER_BUFFER: return TEXT("GL_SAMPLER_BUFFER");
case GL_SAMPLER_2D_RECT: return TEXT("GL_SAMPLER_2D_RECT");
case GL_SAMPLER_2D_RECT_SHADOW: return TEXT("GL_SAMPLER_2D_RECT_SHADOW");
case GL_INT_SAMPLER_1D: return TEXT("GL_INT_SAMPLER_1D");
case GL_INT_SAMPLER_2D: return TEXT("GL_INT_SAMPLER_2D");
case GL_INT_SAMPLER_3D: return TEXT("GL_INT_SAMPLER_3D");
case GL_INT_SAMPLER_CUBE: return TEXT("GL_INT_SAMPLER_CUBE");
case GL_INT_SAMPLER_1D_ARRAY: return TEXT("GL_INT_SAMPLER_1D_ARRAY");
case GL_INT_SAMPLER_2D_ARRAY: return TEXT("GL_INT_SAMPLER_2D_ARRAY");
case GL_INT_SAMPLER_2D_MULTISAMPLE: return TEXT("GL_INT_SAMPLER_2D_MULTISAMPLE");
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return TEXT("GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY");
case GL_INT_SAMPLER_BUFFER: return TEXT("GL_INT_SAMPLER_BUFFER");
case GL_INT_SAMPLER_2D_RECT: return TEXT("GL_INT_SAMPLER_2D_RECT");
case GL_UNSIGNED_INT_SAMPLER_1D: return TEXT("GL_UNSIGNED_INT_SAMPLER_1D");
case GL_UNSIGNED_INT_SAMPLER_2D: return TEXT("GL_UNSIGNED_INT_SAMPLER_2D");
case GL_UNSIGNED_INT_SAMPLER_3D: return TEXT("GL_UNSIGNED_INT_SAMPLER_3D");
case GL_UNSIGNED_INT_SAMPLER_CUBE: return TEXT("GL_UNSIGNED_INT_SAMPLER_CUBE");
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return TEXT("GL_UNSIGNED_INT_SAMPLER_1D_ARRAY");
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return TEXT("GL_UNSIGNED_INT_SAMPLER_2D_ARRAY");
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return TEXT("GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE");
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return TEXT("GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY");
case GL_UNSIGNED_INT_SAMPLER_BUFFER: return TEXT("GL_UNSIGNED_INT_SAMPLER_BUFFER");
case GL_UNSIGNED_INT_SAMPLER_2D_RECT: return TEXT("GL_UNSIGNED_INT_SAMPLER_2D_RECT");
default: return TEXT("!!!unknown!!!");
};
}
#endif
#if ENABLE_OPENGL_FRAMEDUMP
static const TCHAR* GetAttachedBufferName( bool bIsScreenBuffer, GLint DrawBufferIndex )
{
if( bIsScreenBuffer )
{
switch( DrawBufferIndex )
{
case GL_NONE: return TEXT("GL_NONE");
case GL_FRONT_LEFT: return TEXT("GL_FRONT_LEFT");
case GL_FRONT_RIGHT: return TEXT("GL_FRONT_RIGHT");
case GL_BACK_LEFT: return TEXT("GL_BACK_LEFT");
case GL_BACK_RIGHT: return TEXT("GL_BACK_RIGHT");
case GL_FRONT: return TEXT("GL_FRONT");
case GL_BACK: return TEXT("GL_BACK");
case GL_LEFT: return TEXT("GL_LEFT");
case GL_RIGHT: return TEXT("GL_RIGHT");
case GL_FRONT_AND_BACK: return TEXT("GL_FRONT_AND_BACK");
case GL_DEPTH: return TEXT("GL_DEPTH");
case GL_STENCIL: return TEXT("GL_STENCIL");
default: return TEXT("unknown");
}
}
else
{
switch( DrawBufferIndex )
{
case GL_COLOR_ATTACHMENT0: return TEXT("GL_COLOR_ATTACHMENT0");
case GL_COLOR_ATTACHMENT1: return TEXT("GL_COLOR_ATTACHMENT1");
case GL_COLOR_ATTACHMENT2: return TEXT("GL_COLOR_ATTACHMENT2");
case GL_COLOR_ATTACHMENT3: return TEXT("GL_COLOR_ATTACHMENT3");
case GL_COLOR_ATTACHMENT4: return TEXT("GL_COLOR_ATTACHMENT4");
case GL_COLOR_ATTACHMENT5: return TEXT("GL_COLOR_ATTACHMENT5");
case GL_COLOR_ATTACHMENT6: return TEXT("GL_COLOR_ATTACHMENT6");
case GL_COLOR_ATTACHMENT7: return TEXT("GL_COLOR_ATTACHMENT7");
case GL_COLOR_ATTACHMENT8: return TEXT("GL_COLOR_ATTACHMENT8");
case GL_COLOR_ATTACHMENT9: return TEXT("GL_COLOR_ATTACHMENT9");
case GL_COLOR_ATTACHMENT10: return TEXT("GL_COLOR_ATTACHMENT10");
case GL_COLOR_ATTACHMENT11: return TEXT("GL_COLOR_ATTACHMENT11");
case GL_COLOR_ATTACHMENT12: return TEXT("GL_COLOR_ATTACHMENT12");
case GL_COLOR_ATTACHMENT13: return TEXT("GL_COLOR_ATTACHMENT13");
case GL_COLOR_ATTACHMENT14: return TEXT("GL_COLOR_ATTACHMENT14");
case GL_COLOR_ATTACHMENT15: return TEXT("GL_COLOR_ATTACHMENT15");
case GL_DEPTH_ATTACHMENT: return TEXT("GL_DEPTH_ATTACHMENT");
case GL_STENCIL_ATTACHMENT: return TEXT("GL_STENCIL_ATTACHMENT");
default: return TEXT("unknown");
}
}
return NULL; // to shut up the compiler; it can't reach here, ever
}
static const TCHAR* GetGLCompareString( GLint CompareFunction )
{
switch( CompareFunction )
{
case GL_NEVER: return TEXT("GL_NEVER");
case GL_LESS: return TEXT("GL_LESS");
case GL_EQUAL: return TEXT("GL_EQUAL");
case GL_LEQUAL: return TEXT("GL_LEQUAL");
case GL_GREATER: return TEXT("GL_GREATER");
case GL_NOTEQUAL: return TEXT("GL_NOTEQUAL");
case GL_GEQUAL: return TEXT("GL_GEQUAL");
case GL_ALWAYS: return TEXT("GL_ALWAYS");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetGLStencilOpString( GLint StencilOp )
{
switch( StencilOp )
{
case GL_ZERO: return TEXT("GL_ZERO");
case GL_KEEP: return TEXT("GL_KEEP");
case GL_REPLACE: return TEXT("GL_REPLACE");
case GL_INCR: return TEXT("GL_INCR");
case GL_DECR: return TEXT("GL_DECR");
case GL_INCR_WRAP: return TEXT("GL_INCR_WRAP");
case GL_DECR_WRAP: return TEXT("GL_DECR_WRAP");
case GL_INVERT: return TEXT("GL_INVERT");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetGLDataTypeString( GLint DataType )
{
// static FString Unknown;
switch( DataType )
{
case GL_BYTE: return TEXT("GL_BYTE");
case GL_UNSIGNED_BYTE: return TEXT("GL_UNSIGNED_BYTE");
case GL_SHORT: return TEXT("GL_SHORT");
case GL_UNSIGNED_SHORT: return TEXT("GL_UNSIGNED_SHORT");
case GL_INT: return TEXT("GL_INT");
case GL_UNSIGNED_INT: return TEXT("GL_UNSIGNED_INT");
case GL_FLOAT: return TEXT("GL_FLOAT");
case GL_DOUBLE: return TEXT("GL_DOUBLE");
case GL_HALF_FLOAT: return TEXT("GL_HALF_FLOAT");
default: return TEXT("!!!unknown!!!");
// default:
// Unknown = FString::Printf( TEXT("!!!unknown!!!(%d)"), DataType );
// return *Unknown;
}
}
static const TCHAR* GetGLBlendingFactorString( GLint BlendingFactor )
{
switch( BlendingFactor )
{
case GL_ZERO: return TEXT("GL_ZERO");
case GL_ONE: return TEXT("GL_ONE");
case GL_SRC_COLOR: return TEXT("GL_SRC_COLOR");
case GL_ONE_MINUS_SRC_COLOR: return TEXT("GL_ONE_MINUS_SRC_COLOR");
case GL_SRC_ALPHA: return TEXT("GL_SRC_ALPHA");
case GL_ONE_MINUS_SRC_ALPHA: return TEXT("GL_ONE_MINUS_SRC_ALPHA");
case GL_DST_ALPHA: return TEXT("GL_DST_ALPHA");
case GL_ONE_MINUS_DST_ALPHA: return TEXT("GL_ONE_MINUS_DST_ALPHA");
case GL_DST_COLOR: return TEXT("GL_DST_COLOR");
case GL_ONE_MINUS_DST_COLOR: return TEXT("GL_ONE_MINUS_DST_COLOR");
case GL_SRC_ALPHA_SATURATE: return TEXT("GL_SRC_ALPHA_SATURATE");
case GL_CONSTANT_COLOR: return TEXT("GL_CONSTANT_COLOR");
case GL_ONE_MINUS_CONSTANT_COLOR: return TEXT("GL_ONE_MINUS_CONSTANT_COLOR");
case GL_CONSTANT_ALPHA: return TEXT("GL_CONSTANT_ALPHA");
case GL_ONE_MINUS_CONSTANT_ALPHA: return TEXT("GL_ONE_MINUS_CONSTANT_ALPHA");
case GL_BLEND_COLOR: return TEXT("GL_BLEND_COLOR");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetGLBlendFuncString( GLint BlendFunction )
{
switch( BlendFunction )
{
case GL_FUNC_ADD: return TEXT("GL_FUNC_ADD");
case GL_MIN: return TEXT("GL_MIN");
case GL_MAX: return TEXT("GL_MAX");
case GL_FUNC_SUBTRACT: return TEXT("GL_FUNC_SUBTRACT");
case GL_FUNC_REVERSE_SUBTRACT: return TEXT("GL_FUNC_REVERSE_SUBTRACT");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetHintName( GLint Hint )
{
switch( Hint )
{
case GL_DONT_CARE: return TEXT("GL_DONT_CARE");
case GL_FASTEST: return TEXT("GL_FASTEST");
case GL_NICEST: return TEXT("GL_NICEST");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetCompressedTextureFormatName( GLint CompressedTextureFormat )
{
switch( CompressedTextureFormat )
{
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_RGB_S3TC_DXT1_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT1_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT3_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT5_EXT");
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_SRGB_S3TC_DXT1_EXT");
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT");
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return TEXT("GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT");
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return TEXT("GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT");
default: return TEXT("(other)");
};
}
static const TCHAR* GetGLLogicOpString( GLint LogicOp )
{
switch( LogicOp )
{
case GL_CLEAR: return TEXT("GL_CLEAR");
case GL_AND: return TEXT("GL_AND");
case GL_AND_REVERSE: return TEXT("GL_AND_REVERSE");
case GL_COPY: return TEXT("GL_COPY");
case GL_AND_INVERTED: return TEXT("GL_AND_INVERTED");
case GL_NOOP: return TEXT("GL_NOOP");
case GL_XOR: return TEXT("GL_XOR");
case GL_OR: return TEXT("GL_OR");
case GL_NOR: return TEXT("GL_NOR");
case GL_EQUIV: return TEXT("GL_EQUIV");
case GL_INVERT: return TEXT("GL_INVERT");
case GL_OR_REVERSE: return TEXT("GL_OR_REVERSE");
case GL_COPY_INVERTED: return TEXT("GL_COPY_INVERTED");
case GL_OR_INVERTED: return TEXT("GL_OR_INVERTED");
case GL_NAND: return TEXT("GL_NAND");
case GL_SET: return TEXT("GL_SET");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetCullFaceModeName( GLint CullFaceMode )
{
switch( CullFaceMode )
{
case GL_FRONT: return TEXT("GL_FRONT");
case GL_BACK: return TEXT("GL_BACK");
case GL_FRONT_AND_BACK: return TEXT("GL_FRONT_AND_BACK");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetFrontFaceName( GLint FrontFace )
{
switch( FrontFace )
{
case GL_CCW: return TEXT("GL_CCW");
case GL_CW: return TEXT("GL_CW");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetAttachmentSlotName( GLenum AttachmentSlot )
{
switch( AttachmentSlot )
{
case GL_FRONT_LEFT: return TEXT("GL_FRONT_LEFT");
case GL_FRONT_RIGHT: return TEXT("GL_FRONT_RIGHT");
case GL_BACK_LEFT: return TEXT("GL_BACK_LEFT");
case GL_BACK_RIGHT: return TEXT("GL_BACK_RIGHT");
case GL_DEPTH: return TEXT("GL_DEPTH");
case GL_STENCIL: return TEXT("GL_STENCIL");
case GL_COLOR_ATTACHMENT0: return TEXT("GL_COLOR_ATTACHMENT0");
case GL_COLOR_ATTACHMENT1: return TEXT("GL_COLOR_ATTACHMENT1");
case GL_COLOR_ATTACHMENT2: return TEXT("GL_COLOR_ATTACHMENT2");
case GL_COLOR_ATTACHMENT3: return TEXT("GL_COLOR_ATTACHMENT3");
case GL_COLOR_ATTACHMENT4: return TEXT("GL_COLOR_ATTACHMENT4");
case GL_COLOR_ATTACHMENT5: return TEXT("GL_COLOR_ATTACHMENT5");
case GL_COLOR_ATTACHMENT6: return TEXT("GL_COLOR_ATTACHMENT6");
case GL_COLOR_ATTACHMENT7: return TEXT("GL_COLOR_ATTACHMENT7");
case GL_COLOR_ATTACHMENT8: return TEXT("GL_COLOR_ATTACHMENT8");
case GL_COLOR_ATTACHMENT9: return TEXT("GL_COLOR_ATTACHMENT9");
case GL_COLOR_ATTACHMENT10: return TEXT("GL_COLOR_ATTACHMENT10");
case GL_COLOR_ATTACHMENT11: return TEXT("GL_COLOR_ATTACHMENT11");
case GL_COLOR_ATTACHMENT12: return TEXT("GL_COLOR_ATTACHMENT12");
case GL_COLOR_ATTACHMENT13: return TEXT("GL_COLOR_ATTACHMENT13");
case GL_COLOR_ATTACHMENT14: return TEXT("GL_COLOR_ATTACHMENT14");
case GL_COLOR_ATTACHMENT15: return TEXT("GL_COLOR_ATTACHMENT15");
case GL_DEPTH_ATTACHMENT: return TEXT("GL_DEPTH_ATTACHMENT");
case GL_STENCIL_ATTACHMENT: return TEXT("GL_STENCIL_ATTACHMENT");
case GL_DEPTH_STENCIL_ATTACHMENT: return TEXT("GL_DEPTH_STENCIL_ATTACHMENT");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetGLInternalFormatString( GLint InternalFormat )
{
switch( InternalFormat )
{
// Compressed formats
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_RGB_S3TC_DXT1_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT1_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT3_EXT");
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return TEXT("GL_COMPRESSED_RGBA_S3TC_DXT5_EXT");
// Depth/stencil formats
case GL_DEPTH_COMPONENT16: return TEXT("GL_DEPTH_COMPONENT16");
case GL_DEPTH_COMPONENT24: return TEXT("GL_DEPTH_COMPONENT24");
case GL_DEPTH_COMPONENT32F: return TEXT("GL_DEPTH_COMPONENT32F");
case GL_DEPTH24_STENCIL8: return TEXT("GL_DEPTH24_STENCIL8");
case GL_DEPTH32F_STENCIL8: return TEXT("GL_DEPTH32F_STENCIL8");
// RGBA
case GL_RGBA8: return TEXT("GL_RGBA8");
case GL_RGBA12: return TEXT("GL_RGBA12");
case GL_RGBA16: return TEXT("GL_RGBA16");
case GL_RGBA16F: return TEXT("GL_RGBA16F");
case GL_RGBA32F: return TEXT("GL_RGBA32");
case GL_RGBA16I: return TEXT("GL_RGBA16I");
case GL_RGBA16UI: return TEXT("GL_RGBA16UI");
case GL_RGBA32I: return TEXT("GL_RGBA32I");
case GL_RGBA32UI: return TEXT("GL_RGBA32UI");
case GL_RGB10_A2: return TEXT("GL_RGB10_A2");
case GL_RGBA4: return TEXT("GL_RGBA4");
case GL_RGB5_A1: return TEXT("GL_RGB5_A1");
case GL_SRGB8_ALPHA8: return TEXT("GL_SRGB8_ALPHA8");
// RG
case GL_RG8: return TEXT("GL_RG8");
case GL_RG16: return TEXT("GL_RG16");
case GL_RG16F: return TEXT("GL_RG16F");
case GL_RG32F: return TEXT("GL_RG32F");
case GL_RG8I: return TEXT("GL_RG8I");
case GL_RG8UI: return TEXT("GL_RG8UI");
case GL_RG16I: return TEXT("GL_RG16I");
case GL_RG16UI: return TEXT("GL_RG16UI");
case GL_RG32I: return TEXT("GL_RG32I");
case GL_RG32UI: return TEXT("GL_RG32UI");
// R
case GL_R8: return TEXT("GL_R8");
case GL_R16: return TEXT("GL_R16");
case GL_R16F: return TEXT("GL_R16F");
case GL_R32F: return TEXT("GL_R32F");
case GL_R8I: return TEXT("GL_R8I");
case GL_R8UI: return TEXT("GL_R8UI");
case GL_R16I: return TEXT("GL_R16I");
case GL_R16UI: return TEXT("GL_R16UI");
case GL_R32I: return TEXT("GL_R32I");
case GL_R32UI: return TEXT("GL_R32UI");
// RGB (at the end, as it's not expected to be used)
case GL_RGB8: return TEXT("GL_RGB8");
case GL_RGB5: return TEXT("GL_RGB5");
case GL_R3_G3_B2: return TEXT("GL_R3_G3_B2");
case GL_RGB4: return TEXT("GL_RGB4");
case GL_SRGB8: return TEXT("GL_SRGB8");
case GL_R11F_G11F_B10F: return TEXT("GL_R11F_G11F_B10F");
case GL_RGB9_E5: return TEXT("GL_RGB9_E5");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetComponentType( GLint ComponentType )
{
switch( ComponentType )
{
case GL_FLOAT: return TEXT("GL_FLOAT");
case GL_INT: return TEXT("GL_INT");
case GL_UNSIGNED_INT: return TEXT("GL_UNSIGNED_INT");
case GL_SIGNED_NORMALIZED: return TEXT("GL_SIGNED_NORMALIZED");
case GL_UNSIGNED_NORMALIZED: return TEXT("GL_UNSIGNED_NORMALIZED");
default: return TEXT("!!!unknown!!!");
}
}
static const TCHAR* GetColorEncoding( GLint ColorEncoding )
{
switch( ColorEncoding )
{
case GL_LINEAR: return TEXT("GL_LINEAR");
case GL_SRGB: return TEXT("GL_SRGB");
default: return TEXT("!!!unknown!!!");
}
}
static const TCHAR* GetCubeMapFaceName( GLint CubeMapFace )
{
switch( CubeMapFace )
{
case GL_TEXTURE_CUBE_MAP_POSITIVE_X: return TEXT("GL_TEXTURE_CUBE_MAP_POSITIVE_X");
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: return TEXT("GL_TEXTURE_CUBE_MAP_NEGATIVE_X");
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: return TEXT("GL_TEXTURE_CUBE_MAP_POSITIVE_Y");
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return TEXT("GL_TEXTURE_CUBE_MAP_NEGATIVE_Y");
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: return TEXT("GL_TEXTURE_CUBE_MAP_POSITIVE_Z");
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return TEXT("GL_TEXTURE_CUBE_MAP_NEGATIVE_Z");
default: return TEXT("!!!unknown!!!");
}
}
static const TCHAR* GetShaderType( GLint Type )
{
switch( Type )
{
case GL_VERTEX_SHADER: return TEXT("GL_VERTEX_SHADER");
case GL_GEOMETRY_SHADER: return TEXT("GL_GEOMETRY_SHADER");
case GL_FRAGMENT_SHADER: return TEXT("GL_FRAGMENT_SHADER");
default: return TEXT("!!!unknown!!!");
}
}
static const TCHAR* GetGLTextureFilterString( GLint TextureFilter )
{
switch( TextureFilter )
{
case GL_NEAREST: return TEXT("GL_NEAREST");
case GL_LINEAR: return TEXT("GL_LINEAR");
case GL_NEAREST_MIPMAP_NEAREST: return TEXT("GL_NEAREST_MIPMAP_NEAREST");
case GL_LINEAR_MIPMAP_NEAREST: return TEXT("GL_LINEAR_MIPMAP_NEAREST");
case GL_NEAREST_MIPMAP_LINEAR: return TEXT("GL_NEAREST_MIPMAP_LINEAR");
case GL_LINEAR_MIPMAP_LINEAR: return TEXT("GL_LINEAR_MIPMAP_LINEAR");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* GetGLTextureWrapString( GLint TextureWrap )
{
switch( TextureWrap )
{
case GL_REPEAT: return TEXT("GL_REPEAT");
case GL_MIRRORED_REPEAT: return TEXT("GL_MIRRORED_REPEAT");
case GL_CLAMP_TO_EDGE: return TEXT("GL_CLAMP_TO_EDGE");
case GL_CLAMP_TO_BORDER: return TEXT("GL_CLAMP_TO_BORDER");
case GL_MIRROR_CLAMP_EXT: return TEXT("GL_MIRROR_CLAMP_EXT");
default: return TEXT("!!!unknown!!!");
};
}
static const TCHAR* NameOfType( GLint Type )
{
switch( Type )
{
case GL_DOUBLE: return TEXT("GL_DOUBLE");
case GL_FLOAT: return TEXT("GL_FLOAT");
case GL_HALF_FLOAT: return TEXT("GL_HALF_FLOAT");
case GL_UNSIGNED_SHORT: return TEXT("GL_UNSIGNED_SHORT");
case GL_SHORT: return TEXT("GL_SHORT");
case GL_UNSIGNED_BYTE: return TEXT("GL_UNSIGNED_BYTE");
default: return TEXT("!!!unknown!!!");
}
}
static int32 SizeOfType( GLint Type )
{
switch( Type )
{
case GL_DOUBLE: return 8;
case GL_FLOAT: return 4;
case GL_HALF_FLOAT:
case GL_UNSIGNED_SHORT:
case GL_SHORT: return 2;
case GL_UNSIGNED_BYTE: return 1;
default:
check( 0 );
return 0;
}
}
class FOpenGLDebugFrameDumper
{
public:
/** Event call, called from the engine immediately after a draw command */
void SignalDrawEvent( const TCHAR* FolderPart, const TCHAR* DrawCommandDescription, GLint ElementArrayType, GLint StartVertex, GLint VertexCount, GLint InstanceCount );
/** Event call, called from the engine immediately after a clear command */
void SignalClearEvent( int8 ClearType, int8 NumColors, const float* Colors, float Depth, uint32 Stencil );
/** Event call, called from the engine immediately after a framebuffer blit command */
void SignalFramebufferBlitEvent( GLbitfield Mask );
/** Event call, called from the engine immediately after buffer swap/end of frame */
void SignalEndFrameEvent( void );
/** Command to dump information about all events from now until next end frame event (ie. to dump this frame, or partial frame, if frame rendering already started) */
void TriggerFrameDump( void );
static inline FOpenGLDebugFrameDumper* Instance( void )
{
if( !Singleton )
Singleton = new FOpenGLDebugFrameDumper;
return Singleton;
}
protected:
static FOpenGLDebugFrameDumper* Singleton;
struct TextureLevelInfo
{
GLint Width;
GLint Height;
GLint Depth;
GLint Samples;
GLboolean bHasFixedSampleLocations;
GLint InternalFormat;
GLint RedBits;
GLint GreenBits;
GLint BlueBits;
GLint AlphaBits;
GLint DepthBits;
GLint StencilBits;
GLint SharedSize;
GLint RedType;
GLint GreenType;
GLint BlueType;
GLint AlphaType;
GLint DepthType;
GLboolean bIsCompressed;
GLint CompressedSize;
GLint DataStoreBinding;
};
struct EFramebufferAttachmentSlotType
{
enum Type
{
Color,
Depth,
Stencil,
};
};
struct VertexAttribInfo
{
GLint Index;
GLuint Stride;
GLint Type;
GLint Size;
GLint SizeRead;
GLuint Offset;
GLuint OffsetWithinVertex;
bool bInteger;
bool bNormalized;
bool bSkip;
bool bDivisor;
};
static int32 CDECL qsort_compare_VertexAttribInfo( const void* A, const void* B )
{
const VertexAttribInfo* Element1 = (const VertexAttribInfo*)A;
const VertexAttribInfo* Element2 = (const VertexAttribInfo*)B;
return Element1->Offset - Element2->Offset;
}
/** Event counter. Describes how to name the subfolder we dump the next event to. */
uint32 EventCounter;
/** Frame counter. Describes how to name the subfolder we dump the next frame to. */
uint32 FrameCounter;
FString* CachedRootFolder;
FString* CachedFrameFolder;
FString* CachedEventFolder;
bool bDumpingFrame;
FOpenGLDebugFrameDumper( void )
: EventCounter( 0 )
, FrameCounter( 0 )
, CachedRootFolder( NULL )
, CachedFrameFolder( NULL )
, CachedEventFolder( NULL )
, bDumpingFrame( false )
{
}
void DumpRenderTargetsState( FOutputDeviceFile& LogFile );
void DumpDepthState( FOutputDeviceFile& LogFile );
void DumpStencilState( FOutputDeviceFile& LogFile );
void DumpBufferMasks( FOutputDeviceFile& LogFile );
void DumpClearValues( FOutputDeviceFile& LogFile );
void DumpMultisamplingSettings( FOutputDeviceFile& LogFile );
void DumpScissorAndViewport( FOutputDeviceFile& LogFile );
void DumpVertexAttribArraysState( FOutputDeviceFile& LogFile );
void DumpBlendingState( FOutputDeviceFile& LogFile );
void DumpBufferBindings( FOutputDeviceFile& LogFile );
void DumpHintSettings( FOutputDeviceFile& LogFile );
void DumpOpenGLLimits( FOutputDeviceFile& LogFile );
void DumpPointsSettings( FOutputDeviceFile& LogFile );
void DumpLinesSettings( FOutputDeviceFile& LogFile );
void DumpPolygonsSettings( FOutputDeviceFile& LogFile );
void DumpLogicOpsSettings( FOutputDeviceFile& LogFile );
void DumpPixelModeSettings( FOutputDeviceFile& LogFile );
void DumpTextureLimitsAndBindings( FOutputDeviceFile& LogFile );
void DumpProgramSettings( FOutputDeviceFile& LogFile );
void DumpRenderbufferSettings( FOutputDeviceFile& LogFile, GLint RenderbufferID );
void DumpFramebufferAttachmentSettings( FOutputDeviceFile& LogFile, GLenum AttachmentSlot );
void DumpFramebufferSettings( FOutputDeviceFile& LogFile, GLint FramebufferID );
void DumpTextureUnitSettings( FOutputDeviceFile& LogFile, GLint TextureUnitIndex );
void DumpBoundTextureSettings( FOutputDeviceFile& LogFile, GLenum UnitTarget );
void DumpBoundTextureSurfaceSettings( FOutputDeviceFile& LogFile, GLenum SurfaceType, GLint BaseLevel, GLint MaxLevel );
void GetBoundTextureSurfaceLevelSettings( GLenum SurfaceType, GLint Level, TextureLevelInfo& OutInfo );
void DumpBoundSamplerSettings( FOutputDeviceFile& LogFile, GLint SamplerID );
void DumpProgramContents( FOutputDeviceFile& LogFile, GLint ProgramID );
void DumpShaderContents( FOutputDeviceFile& LogFile, GLint ShaderID );
void DumpFramebufferContent( GLint FramebufferID, GLint AttachmentSlot, const TCHAR* TargetFilename, EFramebufferAttachmentSlotType::Type SlotType, bool bShouldFlipImageVertically );
void DumpTextureContentForImageUnit( int32 UnitIndex );
void DumpTextureSurfaceContent( const TCHAR* TargetFilename, GLenum SurfaceType, GLint Level );
void DumpGeneralOpenGLState( const TCHAR* DrawCommandDescription, bool bIsDrawEvent, bool bIsFramebufferBlitEvent );
void DumpFramebufferState( bool bReadFramebuffer );
void DumpProgramAndShaderState( void );
void DumpBoundTextureState( void );
void DumpFramebufferContents( bool bReadFramebuffer );
void DumpBoundTexturesContents( void );
void DumpElementArrayBufferContents( GLenum ElementArrayType );
void DumpRelevantVertexArrayBufferContents( GLint StartVertex, GLint VertexCount, GLint InstanceCount );
void DumpBoundVertexArrayBufferContents( GLint VertexBufferID, GLint StartVertex, GLint VertexCount, GLint InstanceCount );
void InterpretUniform( GLint UniformType, void* DataBuffer, FString& OutAppendString );
void SetNewEventFolder( const FString& EventString );
};
FOpenGLDebugFrameDumper* FOpenGLDebugFrameDumper::Singleton = NULL;
void FOpenGLDebugFrameDumper::DumpRenderTargetsState( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Render Targets") LINE_TERMINATOR );
if ( FOpenGL::SupportsMultipleRenderTargets() )
{
GLint DrawFramebuffer;
glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &DrawFramebuffer );
ASSERT_NO_GL_ERROR();
GLint MaxDrawBuffers = 0;
glGetIntegerv( GL_MAX_DRAW_BUFFERS, &MaxDrawBuffers );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_DRAW_BUFFERS: %d") LINE_TERMINATOR, MaxDrawBuffers );
GLint AttachedBufferIndex;
for( GLint DrawBufferIndex = 0; DrawBufferIndex < MaxDrawBuffers; ++DrawBufferIndex )
{
glGetIntegerv( GL_DRAW_BUFFER0+DrawBufferIndex, &AttachedBufferIndex );
ASSERT_NO_GL_ERROR();
if( AttachedBufferIndex )
{
const TCHAR* AttachedBufferName = GetAttachedBufferName( ( DrawFramebuffer == 0 ), AttachedBufferIndex );
if( AttachedBufferName )
{
LogFile.Logf( TEXT("\t\tGL_DRAW_BUFFER%d: %s") LINE_TERMINATOR, DrawBufferIndex, AttachedBufferName );
}
else
{
LogFile.Logf( TEXT("\t\tGL_DRAW_BUFFER%d: 0x%x") LINE_TERMINATOR, DrawBufferIndex, AttachedBufferIndex );
}
}
}
GLint ReadFramebuffer;
glGetIntegerv( GL_READ_FRAMEBUFFER_BINDING, &ReadFramebuffer );
ASSERT_NO_GL_ERROR();
glGetIntegerv( GL_READ_BUFFER, &AttachedBufferIndex );
ASSERT_NO_GL_ERROR();
const TCHAR* AttachedBufferName = GetAttachedBufferName( ( ReadFramebuffer == 0 ), AttachedBufferIndex );
if( AttachedBufferName )
{
LogFile.Logf( TEXT("\tGL_READ_BUFFER: %s") LINE_TERMINATOR, AttachedBufferName );
}
else
{
LogFile.Logf( TEXT("\tGL_READ_BUFFER: 0x%x") LINE_TERMINATOR, AttachedBufferIndex );
}
}
else
{
//@todo-mobile: More debug info
GLint CurrentFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &CurrentFBO);
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\GL_FRAMEBUFFER_BINDING: %d") LINE_TERMINATOR, CurrentFBO );
}
}
void FOpenGLDebugFrameDumper::DumpDepthState( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Depth") LINE_TERMINATOR );
GLboolean DepthTestEnabled;
glGetBooleanv( GL_DEPTH_TEST, &DepthTestEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_TEST: %s") LINE_TERMINATOR, DepthTestEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLfloat DepthClearValue;
glGetFloatv( GL_DEPTH_CLEAR_VALUE, &DepthClearValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_CLEAR_VALUE: %f") LINE_TERMINATOR, DepthClearValue );
GLint DepthFunction;
glGetIntegerv( GL_DEPTH_FUNC, &DepthFunction );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_FUNC: %s") LINE_TERMINATOR, GetGLCompareString( DepthFunction ) );
GLfloat DepthRange[2];
glGetFloatv( GL_DEPTH_RANGE, DepthRange );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_RANGE: { %f, %f }") LINE_TERMINATOR, DepthRange[0], DepthRange[1] );
GLboolean WriteMask;
glGetBooleanv( GL_DEPTH_WRITEMASK, &WriteMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_WRITEMASK: %s") LINE_TERMINATOR, WriteMask ? TEXT("TRUE") : TEXT("FALSE") );
}
void FOpenGLDebugFrameDumper::DumpStencilState( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Stencil") LINE_TERMINATOR );
GLboolean StencilTestEnabled;
glGetBooleanv( GL_STENCIL_TEST, &StencilTestEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_TEST: %s") LINE_TERMINATOR, StencilTestEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLint ClearValue;
glGetIntegerv( GL_STENCIL_CLEAR_VALUE, &ClearValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_CLEAR_VALUE: 0x%x") LINE_TERMINATOR, ClearValue );
GLint TestFailResult;
glGetIntegerv( GL_STENCIL_FAIL, &TestFailResult );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_FAIL: %s") LINE_TERMINATOR, GetGLStencilOpString( TestFailResult ) );
GLint TestPassDepthFailResult;
glGetIntegerv( GL_STENCIL_PASS_DEPTH_FAIL, &TestPassDepthFailResult );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_PASS_DEPTH_FAIL: %s") LINE_TERMINATOR, GetGLStencilOpString( TestPassDepthFailResult ) );
GLint TestPassDepthPassResult;
glGetIntegerv( GL_STENCIL_PASS_DEPTH_PASS, &TestPassDepthPassResult );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_PASS_DEPTH_PASS: %s") LINE_TERMINATOR, GetGLStencilOpString( TestPassDepthPassResult ) );
GLint CompareFunction;
glGetIntegerv( GL_STENCIL_FUNC, &CompareFunction );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_FUNC: %s") LINE_TERMINATOR, GetGLCompareString( CompareFunction ) );
GLint CompareReference;
glGetIntegerv( GL_STENCIL_REF, &CompareReference );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_REF: 0x%x") LINE_TERMINATOR, CompareReference );
GLint ValueMask;
glGetIntegerv( GL_STENCIL_VALUE_MASK, &ValueMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_VALUE_MASK: 0x%x") LINE_TERMINATOR, ValueMask );
GLint WriteMask;
glGetIntegerv( GL_STENCIL_WRITEMASK, &WriteMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_WRITEMASK: 0x%x") LINE_TERMINATOR, WriteMask );
}
void FOpenGLDebugFrameDumper::DumpBufferMasks( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Buffer Masks") LINE_TERMINATOR );
GLint MaxDrawBuffers = 0;
glGetIntegerv( GL_MAX_DRAW_BUFFERS, &MaxDrawBuffers );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_DRAW_BUFFERS: %d") LINE_TERMINATOR, MaxDrawBuffers );
GLboolean ColorWriteMask[4];
for( GLint DrawBufferIndex = 0; DrawBufferIndex < MaxDrawBuffers; ++DrawBufferIndex )
{
glGetBooleani_v( GL_COLOR_WRITEMASK, DrawBufferIndex, ColorWriteMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_COLOR_WRITEMASK for buffer %d: ( %s, %s, %s, %s )") LINE_TERMINATOR,
DrawBufferIndex,
ColorWriteMask[0] ? TEXT("TRUE") : TEXT("FALSE"),
ColorWriteMask[1] ? TEXT("TRUE") : TEXT("FALSE"),
ColorWriteMask[2] ? TEXT("TRUE") : TEXT("FALSE"),
ColorWriteMask[3] ? TEXT("TRUE") : TEXT("FALSE") );
}
GLboolean DepthWriteMask;
glGetBooleanv( GL_DEPTH_WRITEMASK, &DepthWriteMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_WRITEMASK: %s") LINE_TERMINATOR, DepthWriteMask ? TEXT("TRUE") : TEXT("FALSE") );
GLint StencilValueMask;
glGetIntegerv( GL_STENCIL_VALUE_MASK, &StencilValueMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_VALUE_MASK: 0x%x") LINE_TERMINATOR, StencilValueMask );
GLint StencilWriteMask;
glGetIntegerv( GL_STENCIL_WRITEMASK, &StencilWriteMask );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_WRITEMASK: 0x%x") LINE_TERMINATOR, StencilWriteMask );
// GLint StencilBackWriteMask;
// glGetIntegerv( GL_STENCIL_BACK_WRITEMASK, &StencilBackWriteMask );
// ASSERT_NO_GL_ERROR();
// LogFile.Logf( TEXT("\tGL_STENCIL_BACK_WRITEMASK: 0x%x") LINE_TERMINATOR, StencilBackWriteMask );
}
void FOpenGLDebugFrameDumper::DumpClearValues( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Clear Values") LINE_TERMINATOR );
GLfloat ColorClearValue[4];
glGetFloatv( GL_COLOR_CLEAR_VALUE, ColorClearValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_COLOR_CLEAR_VALUE: ( %f, %f, %f, %f )") LINE_TERMINATOR, ColorClearValue[0], ColorClearValue[1], ColorClearValue[2], ColorClearValue[3] );
GLfloat DepthClearValue;
glGetFloatv( GL_DEPTH_CLEAR_VALUE, &DepthClearValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DEPTH_CLEAR_VALUE: %f") LINE_TERMINATOR, DepthClearValue );
GLint StencilClearValue;
glGetIntegerv( GL_STENCIL_CLEAR_VALUE, &StencilClearValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_STENCIL_CLEAR_VALUE: %d") LINE_TERMINATOR, StencilClearValue );
}
void FOpenGLDebugFrameDumper::DumpMultisamplingSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Multisampling Settings") LINE_TERMINATOR );
GLboolean MultisamplingEnabled;
glGetBooleanv( GL_MULTISAMPLE, &MultisamplingEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MULTISAMPLE: %s") LINE_TERMINATOR, MultisamplingEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean SampleAlphaToCoverage;
glGetBooleanv( GL_SAMPLE_ALPHA_TO_COVERAGE, &SampleAlphaToCoverage );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_ALPHA_TO_COVERAGE: %s") LINE_TERMINATOR, SampleAlphaToCoverage ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean SampleAlphaToOne;
glGetBooleanv( GL_SAMPLE_ALPHA_TO_ONE, &SampleAlphaToOne );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_ALPHA_TO_ONE: %s") LINE_TERMINATOR, SampleAlphaToOne ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean SampleCoverage;
glGetBooleanv( GL_SAMPLE_COVERAGE, &SampleCoverage );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_COVERAGE: %s") LINE_TERMINATOR, SampleCoverage ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean SampleCoverageInvert;
glGetBooleanv( GL_SAMPLE_COVERAGE_INVERT, &SampleCoverageInvert );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_COVERAGE_INVERT: %s") LINE_TERMINATOR, SampleCoverageInvert ? TEXT("Enabled") : TEXT("Disabled") );
GLfloat SampleCoverageValue;
glGetFloatv( GL_SAMPLE_COVERAGE_VALUE, &SampleCoverageValue );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_COVERAGE_VALUE: %f") LINE_TERMINATOR, SampleCoverageValue );
GLint SampleBuffers;
glGetIntegerv( GL_SAMPLE_BUFFERS, &SampleBuffers );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLE_BUFFERS: %d") LINE_TERMINATOR, SampleBuffers );
GLint Samples;
glGetIntegerv( GL_SAMPLES, &Samples );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SAMPLES: %d") LINE_TERMINATOR, Samples );
}
void FOpenGLDebugFrameDumper::DumpScissorAndViewport( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Scissor & Viewport") LINE_TERMINATOR );
GLboolean ScissorTestEnabled;
glGetBooleanv( GL_SCISSOR_TEST, &ScissorTestEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SCISSOR_TEST: %s") LINE_TERMINATOR, ScissorTestEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLint ScissorBox[4];
glGetIntegerv( GL_SCISSOR_BOX, ScissorBox );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SCISSOR_BOX: { %d, %d, %d, %d }") LINE_TERMINATOR, ScissorBox[0], ScissorBox[1], ScissorBox[2], ScissorBox[3] );
GLint Viewport[4];
glGetIntegerv( GL_VIEWPORT, Viewport );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_VIEWPORT: { %d, %d, %d, %d }") LINE_TERMINATOR, Viewport[0], Viewport[1], Viewport[2], Viewport[3] );
GLint MaxClipPlanes;
glGetIntegerv( GL_MAX_CLIP_DISTANCES, &MaxClipPlanes );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_CLIP_DISTANCES: %d") LINE_TERMINATOR, MaxClipPlanes );
for( int32 ClipPlaneIndex = 0; ClipPlaneIndex < MaxClipPlanes; ++ClipPlaneIndex )
{
GLboolean Enabled = glIsEnabled( GL_CLIP_DISTANCE0+ClipPlaneIndex );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_CLIP_DISTANCE%d: %s") LINE_TERMINATOR, ClipPlaneIndex, Enabled ? TEXT("Enabled") : TEXT("Disabled") );
}
}
void FOpenGLDebugFrameDumper::DumpVertexAttribArraysState( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Vertex Attrib Arrays") LINE_TERMINATOR );
// Get generic vertex array count
GLint MaxVertexAttribs = 0;
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &MaxVertexAttribs );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VERTEX_ATTRIBS: %d") LINE_TERMINATOR, MaxVertexAttribs );
// For each generic vertex array, get info
for( GLint VertexAttribArrayIndex = 0; VertexAttribArrayIndex < MaxVertexAttribs; ++VertexAttribArrayIndex )
{
GLint VertexAttribArrayEnabled;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &VertexAttribArrayEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tVertex Attrib Array %d") LINE_TERMINATOR, VertexAttribArrayIndex );
if( VertexAttribArrayEnabled )
{
LogFile.Log( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_ENABLED: TRUE") LINE_TERMINATOR );
GLint VertexAttribArraySize;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_SIZE, &VertexAttribArraySize );
ASSERT_NO_GL_ERROR();
if( VertexAttribArraySize == GL_BGRA )
{
LogFile.Log( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_SIZE: GL_BGRA(4)") LINE_TERMINATOR );
}
else
{
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_SIZE: %d") LINE_TERMINATOR, VertexAttribArraySize );
}
GLint VertexAttribArrayStride;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &VertexAttribArrayStride );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_STRIDE: %d") LINE_TERMINATOR, VertexAttribArrayStride );
GLint VertexAttribArrayType;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_TYPE, &VertexAttribArrayType );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_TYPE: %s") LINE_TERMINATOR, GetGLDataTypeString( VertexAttribArrayType ) );
GLint VertexAttribArrayNormalized;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &VertexAttribArrayNormalized );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_NORMALIZED: %s") LINE_TERMINATOR, VertexAttribArrayNormalized ? TEXT("TRUE") : TEXT("FALSE") );
GLvoid* VertexAttribArrayPointer;
glGetVertexAttribPointerv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER, &VertexAttribArrayPointer );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_POINTER: 0x%x") LINE_TERMINATOR, VertexAttribArrayPointer );
GLint VertexAttribArrayBufferBinding;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &VertexAttribArrayBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: %d") LINE_TERMINATOR, VertexAttribArrayBufferBinding );
GLint VertexAttribArrayIsInteger;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_INTEGER, &VertexAttribArrayIsInteger );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_INTEGER: %s") LINE_TERMINATOR, VertexAttribArrayIsInteger ? TEXT("TRUE") : TEXT("FALSE") );
GLint VertexAttribArrayDivisor;
glGetVertexAttribiv( VertexAttribArrayIndex, GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB, &VertexAttribArrayDivisor );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_DIVISOR: %d") LINE_TERMINATOR, VertexAttribArrayDivisor );
}
else
{
LogFile.Log( TEXT("\t\tGL_VERTEX_ATTRIB_ARRAY_ENABLED: FALSE") LINE_TERMINATOR );
if( VertexAttribArrayIndex )
{
GLfloat CurrentVertexAttribFloat[4];
glGetVertexAttribfv( VertexAttribArrayIndex, GL_CURRENT_VERTEX_ATTRIB, CurrentVertexAttribFloat );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_CURRENT_VERTEX_ATTRIB (assumming float): { %f, %f, %f, %f }") LINE_TERMINATOR, CurrentVertexAttribFloat[0], CurrentVertexAttribFloat[1], CurrentVertexAttribFloat[2], CurrentVertexAttribFloat[3] );
GLuint CurrentVertexAttribInteger[4];
glGetVertexAttribIuiv( VertexAttribArrayIndex, GL_CURRENT_VERTEX_ATTRIB, CurrentVertexAttribInteger );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_CURRENT_VERTEX_ATTRIB (assumming uint32): { %f, %f, %f, %f }") LINE_TERMINATOR, CurrentVertexAttribInteger[0], CurrentVertexAttribInteger[1], CurrentVertexAttribInteger[2], CurrentVertexAttribInteger[3] );
}
else
{
LogFile.Log( TEXT("\t\tVertex attrib array disabled for vertex array zero. Make sure the shader isn't trying to use gl_Position, as this won't make much sense.") LINE_TERMINATOR );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Vertex attrib array is disabled for array zero. This makes sense only if the draw doesn't use vertex buffers at all, relying on vertex id and instance id instead.") );
}
}
}
}
void FOpenGLDebugFrameDumper::DumpBlendingState( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Blending State") LINE_TERMINATOR );
GLboolean bIsEnabled = glIsEnabled( GL_BLEND );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_BLEND: %s") LINE_TERMINATOR, bIsEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLint BlendSourceRGB;
glGetIntegerv( GL_BLEND_SRC_RGB, &BlendSourceRGB );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_SRC_RGB: %s") LINE_TERMINATOR, GetGLBlendingFactorString( BlendSourceRGB ) );
GLint BlendSourceAlpha;
glGetIntegerv( GL_BLEND_SRC_ALPHA, &BlendSourceAlpha );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_SRC_ALPHA: %s") LINE_TERMINATOR, GetGLBlendingFactorString( BlendSourceAlpha ) );
GLint BlendDestinationRGB;
glGetIntegerv( GL_BLEND_DST_RGB, &BlendDestinationRGB );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_DST_RGB: %s") LINE_TERMINATOR, GetGLBlendingFactorString( BlendDestinationRGB ) );
GLint BlendDestinationAlpha;
glGetIntegerv( GL_BLEND_DST_ALPHA, &BlendDestinationAlpha );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_DST_ALPHA: %s") LINE_TERMINATOR, GetGLBlendingFactorString( BlendDestinationAlpha ) );
GLfloat BlendColor[4];
glGetFloatv( GL_BLEND_COLOR, BlendColor );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_COLOR: ( %f, %f, %f, %f )") LINE_TERMINATOR, BlendColor[0], BlendColor[1], BlendColor[2], BlendColor[3] );
GLint BlendEquationRGB;
glGetIntegerv( GL_BLEND_EQUATION_RGB, &BlendEquationRGB );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_EQUATION_RGB: %s") LINE_TERMINATOR, GetGLBlendFuncString( BlendEquationRGB ) );
GLint BlendEquationAlpha;
glGetIntegerv( GL_BLEND_EQUATION_ALPHA, &BlendEquationAlpha );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_BLEND_EQUATION_ALPHA: %s") LINE_TERMINATOR, GetGLBlendFuncString( BlendEquationAlpha ) );
}
void FOpenGLDebugFrameDumper::DumpBufferBindings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Buffer Object Bindings") LINE_TERMINATOR );
GLint ArrayBufferBinding;
glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &ArrayBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_ARRAY_BUFFER_BINDING: %d") LINE_TERMINATOR, ArrayBufferBinding );
GLint ElementArrayBufferBinding;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &ElementArrayBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_ELEMENT_ARRAY_BUFFER_BINDING: %d") LINE_TERMINATOR, ElementArrayBufferBinding );
GLint UniformBufferBinding;
glGetIntegerv( GL_UNIFORM_BUFFER_BINDING, &UniformBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNIFORM_BUFFER_BINDING: %d") LINE_TERMINATOR, UniformBufferBinding );
GLint MaxUniformBuffers = 0;
glGetIntegerv( GL_MAX_UNIFORM_BUFFER_BINDINGS, &MaxUniformBuffers );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_UNIFORM_BUFFER_BINDINGS: %d") LINE_TERMINATOR, MaxUniformBuffers );
#if PLATFORM_MAC
// A workaround for apparent bug in NVidia GT650M driver, introduced in 10.8.2 Mac update.
if( MaxUniformBuffers > 60 )
MaxUniformBuffers = 60;
#endif
for( GLint UniformBufferIndex = 0; UniformBufferIndex < MaxUniformBuffers; ++UniformBufferIndex )
{
GLint UniformBufferBound;
glGetIntegeri_v( GL_UNIFORM_BUFFER_BINDING, UniformBufferIndex, &UniformBufferBound );
ASSERT_NO_GL_ERROR();
if( UniformBufferBound )
{
GLint UniformBufferStart;
glGetIntegeri_v( GL_UNIFORM_BUFFER_START, UniformBufferIndex, &UniformBufferStart );
ASSERT_NO_GL_ERROR();
GLint UniformBufferSize;
glGetIntegeri_v( GL_UNIFORM_BUFFER_SIZE, UniformBufferIndex, &UniformBufferSize );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tIndexed GL_UNIFORM_BUFFER_BINDING for index %d: %d ( start: %d, size: %d )") LINE_TERMINATOR, UniformBufferIndex, UniformBufferBound, UniformBufferStart, UniformBufferSize );
}
}
GLint PixelPackBufferBinding;
glGetIntegerv( GL_PIXEL_PACK_BUFFER_BINDING, &PixelPackBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PIXEL_PACK_BUFFER_BINDING: %d") LINE_TERMINATOR, PixelPackBufferBinding );
GLint PixelUnpackBufferBinding;
glGetIntegerv( GL_PIXEL_UNPACK_BUFFER_BINDING, &PixelUnpackBufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PIXEL_UNPACK_BUFFER_BINDING: %d") LINE_TERMINATOR, PixelUnpackBufferBinding );
GLint DrawFramebufferBinding;
glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &DrawFramebufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DRAW_FRAMEBUFFER_BINDING: %d") LINE_TERMINATOR, DrawFramebufferBinding );
GLint ReadFramebufferBinding;
glGetIntegerv( GL_READ_FRAMEBUFFER_BINDING, &ReadFramebufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_READ_FRAMEBUFFER_BINDING: %d") LINE_TERMINATOR, ReadFramebufferBinding );
GLint RenderbufferBinding;
glGetIntegerv( GL_RENDERBUFFER_BINDING, &RenderbufferBinding );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_RENDERBUFFER_BINDING: %d") LINE_TERMINATOR, RenderbufferBinding );
}
void FOpenGLDebugFrameDumper::DumpHintSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Hints") LINE_TERMINATOR );
{
GLint LineSmoothHint;
glGetIntegerv( GL_LINE_SMOOTH_HINT, &LineSmoothHint );
ASSERT_NO_GL_ERROR();
const TCHAR* HintName = GetHintName( LineSmoothHint );
if( HintName )
{
LogFile.Logf( TEXT("\tGL_LINE_SMOOTH_HINT: %s") LINE_TERMINATOR, HintName );
}
else
{
LogFile.Logf( TEXT("\tGL_LINE_SMOOTH_HINT: 0x%x") LINE_TERMINATOR, LineSmoothHint );
}
}
{
GLint PolygonSmoothHint;
glGetIntegerv( GL_POLYGON_SMOOTH_HINT, &PolygonSmoothHint );
ASSERT_NO_GL_ERROR();
const TCHAR* HintName = GetHintName( PolygonSmoothHint );
if( HintName )
{
LogFile.Logf( TEXT("\tGL_POLYGON_SMOOTH_HINT: %s") LINE_TERMINATOR, HintName );
}
else
{
LogFile.Logf( TEXT("\tGL_POLYGON_SMOOTH_HINT: 0x%x") LINE_TERMINATOR, PolygonSmoothHint );
}
}
{
GLint TextureCompressionHint;
glGetIntegerv( GL_TEXTURE_COMPRESSION_HINT, &TextureCompressionHint );
ASSERT_NO_GL_ERROR();
const TCHAR* HintName = GetHintName( TextureCompressionHint );
if( HintName )
{
LogFile.Logf( TEXT("\tGL_TEXTURE_COMPRESSION_HINT: %s") LINE_TERMINATOR, HintName );
}
else
{
LogFile.Logf( TEXT("\tGL_TEXTURE_COMPRESSION_HINT: 0x%x") LINE_TERMINATOR, TextureCompressionHint );
}
}
{
GLint FragmentShaderDerivativeHint;
glGetIntegerv( GL_FRAGMENT_SHADER_DERIVATIVE_HINT, &FragmentShaderDerivativeHint );
ASSERT_NO_GL_ERROR();
const TCHAR* HintName = GetHintName( FragmentShaderDerivativeHint );
if( HintName )
{
LogFile.Logf( TEXT("\tGL_FRAGMENT_SHADER_DERIVATIVE_HINT: %s") LINE_TERMINATOR, HintName );
}
else
{
LogFile.Logf( TEXT("\tGL_FRAGMENT_SHADER_DERIVATIVE_HINT: 0x%x") LINE_TERMINATOR, FragmentShaderDerivativeHint );
}
}
}
void FOpenGLDebugFrameDumper::DumpOpenGLLimits( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Limits") LINE_TERMINATOR );
GLint SubpixelBits;
glGetIntegerv( GL_SUBPIXEL_BITS, &SubpixelBits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_SUBPIXEL_BIT: %d") LINE_TERMINATOR, SubpixelBits );
GLint Max3DTextureSize;
glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &Max3DTextureSize );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_3D_TEXTURE_SIZE: %d") LINE_TERMINATOR, Max3DTextureSize );
GLint MaxCombinedTextureImageUnits;
glGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &MaxCombinedTextureImageUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d") LINE_TERMINATOR, MaxCombinedTextureImageUnits );
GLint MaxCubeMapTextureSize;
glGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE, &MaxCubeMapTextureSize );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_CUBE_MAP_TEXTURE_SIZE: %d") LINE_TERMINATOR, MaxCubeMapTextureSize );
GLint MaxElementsIndices;
glGetIntegerv( GL_MAX_ELEMENTS_INDICES, &MaxElementsIndices );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_ELEMENTS_INDICES: %d") LINE_TERMINATOR, MaxElementsIndices );
GLint MaxElementsVertices;
glGetIntegerv( GL_MAX_ELEMENTS_VERTICES, &MaxElementsVertices );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_ELEMENTS_VERTICES: %d") LINE_TERMINATOR, MaxElementsVertices );
GLint MaxFragmentUniformComponents;
glGetIntegerv( GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &MaxFragmentUniformComponents );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_FRAGMENT_UNIFORM_COMPONENTS: %d") LINE_TERMINATOR, MaxFragmentUniformComponents );
GLint MaxTextureImageUnits;
glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &MaxTextureImageUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_TEXTURE_IMAGE_UNITS: %d") LINE_TERMINATOR, MaxTextureImageUnits );
GLint MaxTextureLODBias;
glGetIntegerv( GL_MAX_TEXTURE_LOD_BIAS, &MaxTextureLODBias );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_TEXTURE_LOD_BIAS: %d") LINE_TERMINATOR, MaxTextureLODBias );
if( FOpenGL::SupportsTextureFilterAnisotropic())
{
GLint MaxTextureMaxAnisotropy;
glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxTextureMaxAnisotropy );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: %d") LINE_TERMINATOR, MaxTextureMaxAnisotropy );
}
GLint MaxTextureSize;
glGetIntegerv( GL_MAX_TEXTURE_SIZE, &MaxTextureSize );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_TEXTURE_SIZE: %d") LINE_TERMINATOR, MaxTextureSize );
GLint MaxVertexAttribs;
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &MaxVertexAttribs );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VERTEX_ATTRIBS: %d") LINE_TERMINATOR, MaxVertexAttribs );
GLint MaxVertexTextureImageUnits;
glGetIntegerv( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &MaxVertexTextureImageUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %d") LINE_TERMINATOR, MaxVertexTextureImageUnits );
GLint MaxVertexUniformComponents;
glGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &MaxVertexUniformComponents );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VERTEX_UNIFORM_COMPONENTS: %d") LINE_TERMINATOR, MaxVertexUniformComponents );
GLint MaxViewportDimensions[2];
glGetIntegerv( GL_MAX_VIEWPORT_DIMS, MaxViewportDimensions );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VIEWPORT_DIMS: { %d, %d }") LINE_TERMINATOR, MaxViewportDimensions[0], MaxViewportDimensions[1] );
GLint NumCompressedTextureFormats;
glGetIntegerv( GL_NUM_COMPRESSED_TEXTURE_FORMATS, &NumCompressedTextureFormats );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_NUM_COMPRESSED_TEXTURE_FORMATS: %d") LINE_TERMINATOR, NumCompressedTextureFormats );
if( NumCompressedTextureFormats )
{
LogFile.Logf( TEXT("\t{") LINE_TERMINATOR );
GLint* CompressedTextureFormatsTable = (GLint*)FMemory::Malloc( NumCompressedTextureFormats * sizeof( GLint ) );
glGetIntegerv( GL_COMPRESSED_TEXTURE_FORMATS, CompressedTextureFormatsTable );
ASSERT_NO_GL_ERROR();
for( GLint CompressedTextureFormatIndex = 0; CompressedTextureFormatIndex < NumCompressedTextureFormats; ++CompressedTextureFormatIndex )
{
const TCHAR* FormatName = GetCompressedTextureFormatName( CompressedTextureFormatsTable[CompressedTextureFormatIndex] );
if( FormatName )
LogFile.Logf( TEXT("\t\t%s") LINE_TERMINATOR, FormatName );
else
LogFile.Logf( TEXT("\t\t0x%x") LINE_TERMINATOR, CompressedTextureFormatsTable[CompressedTextureFormatIndex] );
}
FMemory::Free( CompressedTextureFormatsTable );
LogFile.Logf( TEXT("\t}") LINE_TERMINATOR );
}
}
void FOpenGLDebugFrameDumper::DumpLinesSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Lines") LINE_TERMINATOR );
GLboolean bLineSmoothEnabled;
glGetBooleanv( GL_LINE_SMOOTH, &bLineSmoothEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LINE_SMOOTH: %s") LINE_TERMINATOR, bLineSmoothEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLfloat LineWidth;
glGetFloatv( GL_LINE_WIDTH, &LineWidth );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LINE_WIDTH: %f") LINE_TERMINATOR, LineWidth );
GLfloat LineWidthGranularity;
glGetFloatv( GL_LINE_WIDTH_GRANULARITY, &LineWidthGranularity );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LINE_WIDTH_GRANULARITY: %f") LINE_TERMINATOR, LineWidthGranularity );
GLfloat LineWidthRange[2];
glGetFloatv( GL_LINE_WIDTH_RANGE, LineWidthRange );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LINE_WIDTH_RANGE: { %f, %f }") LINE_TERMINATOR, LineWidthRange[0], LineWidthRange[1] );
GLfloat AliasedLineWidthRange[2];
glGetFloatv( GL_ALIASED_LINE_WIDTH_RANGE, AliasedLineWidthRange );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_ALIASED_LINE_WIDTH_RANGE: { %f, %f }") LINE_TERMINATOR, AliasedLineWidthRange[0], AliasedLineWidthRange[1] );
}
void FOpenGLDebugFrameDumper::DumpLogicOpsSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Logic Ops") LINE_TERMINATOR );
GLboolean ColorLogicOp;
glGetBooleanv( GL_COLOR_LOGIC_OP, &ColorLogicOp );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_COLOR_LOGIC_OP: %s") LINE_TERMINATOR, ColorLogicOp ? TEXT("Enabled") : TEXT("Disabled") );
GLint LogicOpMode;
glGetIntegerv( GL_LOGIC_OP_MODE, &LogicOpMode );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LOGIC_OP_MODE: %s") LINE_TERMINATOR, GetGLLogicOpString( LogicOpMode ) );
}
void FOpenGLDebugFrameDumper::DumpPixelModeSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Pixel Mode") LINE_TERMINATOR );
GLint PackAlignment;
glGetIntegerv( GL_PACK_ALIGNMENT, &PackAlignment );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_ALIGNMENT: %d") LINE_TERMINATOR, PackAlignment );
GLint PackImageHeight;
glGetIntegerv( GL_PACK_IMAGE_HEIGHT, &PackImageHeight );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_IMAGE_HEIGHT: %d") LINE_TERMINATOR, PackImageHeight );
GLboolean bPackLSBFirst;
glGetBooleanv( GL_PACK_LSB_FIRST, &bPackLSBFirst );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_LSB_FIRST: %s") LINE_TERMINATOR, bPackLSBFirst ? TEXT("TRUE") : TEXT("FALSE") );
GLint PackRowLength;
glGetIntegerv( GL_PACK_ROW_LENGTH, &PackRowLength );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_ROW_LENGTH: %d") LINE_TERMINATOR, PackRowLength );
GLint PackSkipImages;
glGetIntegerv( GL_PACK_SKIP_IMAGES, &PackSkipImages );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_SKIP_IMAGES: %d") LINE_TERMINATOR, PackSkipImages );
GLint PackSkipPixels;
glGetIntegerv( GL_PACK_SKIP_PIXELS, &PackSkipPixels );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_SKIP_PIXELS: %d") LINE_TERMINATOR, PackSkipPixels );
GLint PackSkipRows;
glGetIntegerv( GL_PACK_SKIP_ROWS, &PackSkipRows );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_SKIP_ROWS: %d") LINE_TERMINATOR, PackSkipRows );
GLboolean bPackSwapBytes;
glGetBooleanv( GL_PACK_SWAP_BYTES, &bPackSwapBytes );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_PACK_SWAP_BYTES: %s") LINE_TERMINATOR, bPackSwapBytes ? TEXT("TRUE") : TEXT("FALSE") );
GLint UnpackAlignment;
glGetIntegerv( GL_UNPACK_ALIGNMENT, &UnpackAlignment );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_ALIGNMENTS: %d") LINE_TERMINATOR, UnpackAlignment );
#if PLATFORM_MAC
GLboolean bUnpackClientStorageApple;
glGetBooleanv( GL_UNPACK_CLIENT_STORAGE_APPLE, &bUnpackClientStorageApple );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_CLIENT_STORAGE_APPLE: %s") LINE_TERMINATOR, bUnpackClientStorageApple ? TEXT("TRUE") : TEXT("FALSE") );
#endif
GLint UnpackImageHeight;
glGetIntegerv( GL_UNPACK_IMAGE_HEIGHT, &UnpackImageHeight );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_IMAGE_HEIGHT: %d") LINE_TERMINATOR, UnpackImageHeight );
GLboolean bUnpackLSBFirst;
glGetBooleanv( GL_UNPACK_LSB_FIRST, &bUnpackLSBFirst );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_LSB_FIRST: %s") LINE_TERMINATOR, bUnpackLSBFirst ? TEXT("TRUE") : TEXT("FALSE") );
GLint UnpackRowLength;
glGetIntegerv( GL_UNPACK_ROW_LENGTH, &UnpackRowLength );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_ROW_LENGTH: %d") LINE_TERMINATOR, UnpackRowLength );
GLint UnpackSkipImages;
glGetIntegerv( GL_UNPACK_SKIP_IMAGES, &UnpackSkipImages );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_SKIP_IMAGES: %d") LINE_TERMINATOR, UnpackSkipImages );
GLint UnpackSkipRows;
glGetIntegerv( GL_UNPACK_SKIP_ROWS, &UnpackSkipRows );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_SKIP_ROWS: %d") LINE_TERMINATOR, UnpackSkipRows );
GLboolean bUnpackSwapBytes;
glGetBooleanv( GL_UNPACK_SWAP_BYTES, &bUnpackSwapBytes );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_UNPACK_SWAP_BYTES: %s") LINE_TERMINATOR, bUnpackSwapBytes ? TEXT("TRUE") : TEXT("FALSE") );
}
void FOpenGLDebugFrameDumper::DumpPointsSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Points") LINE_TERMINATOR );
GLfloat PointSize;
glGetFloatv( GL_POINT_SIZE, &PointSize );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POINT_SIZE: %f") LINE_TERMINATOR, PointSize );
GLfloat PointSizeRange[2];
glGetFloatv( GL_POINT_SIZE_RANGE, PointSizeRange );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POINT_SIZE_RANGE: { %f, %f }") LINE_TERMINATOR, PointSizeRange[0], PointSizeRange[1] );
}
void FOpenGLDebugFrameDumper::DumpPolygonsSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Polygons") LINE_TERMINATOR );
GLfloat PolygonOffsetFactor;
glGetFloatv( GL_POLYGON_OFFSET_FACTOR, &PolygonOffsetFactor );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_OFFSET_FACTOR: %f") LINE_TERMINATOR, PolygonOffsetFactor );
GLboolean bCullFaceEnabled;
glGetBooleanv( GL_CULL_FACE, &bCullFaceEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_CULL_FACE: %s") LINE_TERMINATOR, bCullFaceEnabled ? TEXT("TRUE") : TEXT("FALSE") );
GLint CullFaceMode;
glGetIntegerv( GL_CULL_FACE_MODE, &CullFaceMode );
ASSERT_NO_GL_ERROR();
const TCHAR* CullFaceModeName = GetCullFaceModeName( CullFaceMode );
if( CullFaceModeName )
LogFile.Logf( TEXT("\tGL_CULL_FACE_MODE: %s") LINE_TERMINATOR, CullFaceModeName );
else
LogFile.Logf( TEXT("\tGL_CULL_FACE_MODE: 0x%x") LINE_TERMINATOR, CullFaceMode );
GLint FrontFace;
glGetIntegerv( GL_FRONT_FACE, &FrontFace );
ASSERT_NO_GL_ERROR();
const TCHAR* FrontFaceName = GetFrontFaceName( FrontFace );
if( FrontFaceName )
LogFile.Logf( TEXT("\tGL_FRONT_FACE: %s") LINE_TERMINATOR, FrontFaceName );
else
LogFile.Logf( TEXT("\tGL_FRONT_FACE: 0x%x") LINE_TERMINATOR, FrontFace );
GLboolean bPolygonOffsetFillEnabled;
glGetBooleanv( GL_POLYGON_OFFSET_FILL, &bPolygonOffsetFillEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_OFFSET_FILL: %s") LINE_TERMINATOR, bPolygonOffsetFillEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean bPolygonOffsetLineEnabled;
glGetBooleanv( GL_POLYGON_OFFSET_LINE, &bPolygonOffsetLineEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_OFFSET_LINE: %s") LINE_TERMINATOR, bPolygonOffsetLineEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLboolean bPolygonOffsetPointEnabled;
glGetBooleanv( GL_POLYGON_OFFSET_POINT, &bPolygonOffsetPointEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_OFFSET_POINT: %s") LINE_TERMINATOR, bPolygonOffsetPointEnabled ? TEXT("Enabled") : TEXT("Disabled") );
GLfloat PolygonOffsetUnits;
glGetFloatv( GL_POLYGON_OFFSET_UNITS, &PolygonOffsetUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_OFFSET_UNITS: %f") LINE_TERMINATOR, PolygonOffsetUnits );
GLboolean bPolygonSmoothEnabled;
glGetBooleanv( GL_POLYGON_SMOOTH, &bPolygonSmoothEnabled );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_POLYGON_SMOOTH: %s") LINE_TERMINATOR, bPolygonSmoothEnabled ? TEXT("Enabled") : TEXT("Disabled") );
}
void FOpenGLDebugFrameDumper::DumpTextureLimitsAndBindings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Texture Limits And Bindings") LINE_TERMINATOR );
GLint ActiveTextureUnit;
glGetIntegerv( GL_ACTIVE_TEXTURE, &ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_ACTIVE_TEXTURE: %d") LINE_TERMINATOR, ActiveTextureUnit-GL_TEXTURE0 );
GLint MaxTextureImageUnits;
glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &MaxTextureImageUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_TEXTURE_IMAGE_UNITS: %d") LINE_TERMINATOR, MaxTextureImageUnits );
GLint MaxVertexTextureImageUnits;
glGetIntegerv( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &MaxVertexTextureImageUnits );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %d") LINE_TERMINATOR, MaxVertexTextureImageUnits );
for( GLint TextureImageUnitIndex = 0; TextureImageUnitIndex < MaxTextureImageUnits; ++TextureImageUnitIndex )
{
glActiveTexture( GL_TEXTURE0+TextureImageUnitIndex );
ASSERT_NO_GL_ERROR();
GLint TextureBinding1D;
glGetIntegerv( GL_TEXTURE_BINDING_1D, &TextureBinding1D );
ASSERT_NO_GL_ERROR();
if( TextureBinding1D )
{
LogFile.Logf( TEXT("\t\tUnit %2d : GL_TEXTURE_BINDING_1D: %d") LINE_TERMINATOR, TextureImageUnitIndex, TextureBinding1D );
}
GLint TextureBinding2D;
glGetIntegerv( GL_TEXTURE_BINDING_2D, &TextureBinding2D );
ASSERT_NO_GL_ERROR();
if( TextureBinding2D )
{
LogFile.Logf( TEXT("\t\tUnit %2d : GL_TEXTURE_BINDING_2D: %d") LINE_TERMINATOR, TextureImageUnitIndex, TextureBinding2D );
}
GLint TextureBinding3D;
glGetIntegerv( GL_TEXTURE_BINDING_3D, &TextureBinding3D );
ASSERT_NO_GL_ERROR();
if( TextureBinding3D )
{
LogFile.Logf( TEXT("\t\tUnit %2d : GL_TEXTURE_BINDING_3D: %d") LINE_TERMINATOR, TextureImageUnitIndex, TextureBinding3D );
}
GLint TextureBindingCubeMap;
glGetIntegerv( GL_TEXTURE_BINDING_CUBE_MAP, &TextureBindingCubeMap );
ASSERT_NO_GL_ERROR();
if( TextureBindingCubeMap )
{
LogFile.Logf( TEXT("\t\tUnit %2d : GL_TEXTURE_BINDING_CUBE_MAP: %d") LINE_TERMINATOR, TextureImageUnitIndex, TextureBindingCubeMap );
}
}
glActiveTexture( ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
}
void FOpenGLDebugFrameDumper::DumpProgramSettings( FOutputDeviceFile& LogFile )
{
LogFile.Log( TEXT("Program") LINE_TERMINATOR );
GLint CurrentProgram;
glGetIntegerv( GL_CURRENT_PROGRAM, &CurrentProgram );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_CURRENT_PROGRAM: %d") LINE_TERMINATOR, CurrentProgram );
}
void FOpenGLDebugFrameDumper::DumpRenderbufferSettings( FOutputDeviceFile& LogFile, GLint RenderbufferID )
{
GLint CurrentlyBoundRenderbuffer;
glGetIntegerv( GL_RENDERBUFFER_BINDING, &CurrentlyBoundRenderbuffer );
ASSERT_NO_GL_ERROR();
GLboolean bIsRenderbuffer = glIsRenderbuffer( RenderbufferID );
ASSERT_NO_GL_ERROR();
if( !bIsRenderbuffer )
{
LogFile.Logf( TEXT("\t\t\tRenderbuffer ID %d is not a valid renderbuffer ID!") LINE_TERMINATOR, RenderbufferID );
return;
}
if( RenderbufferID != CurrentlyBoundRenderbuffer )
{
glBindRenderbuffer( GL_RENDERBUFFER, RenderbufferID );
ASSERT_NO_GL_ERROR();
}
LogFile.Logf( TEXT("\t\t\tRenderbuffer object %d info") LINE_TERMINATOR, RenderbufferID );
GLint Width;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &Width );
ASSERT_NO_GL_ERROR();
GLint Height;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &Height );
ASSERT_NO_GL_ERROR();
GLint Format;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &Format );
ASSERT_NO_GL_ERROR();
GLint RedSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_RED_SIZE, &RedSize );
ASSERT_NO_GL_ERROR();
GLint GreenSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_GREEN_SIZE, &GreenSize );
ASSERT_NO_GL_ERROR();
GLint BlueSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_BLUE_SIZE, &BlueSize );
ASSERT_NO_GL_ERROR();
GLint AlphaSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &AlphaSize );
ASSERT_NO_GL_ERROR();
GLint DepthSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_DEPTH_SIZE, &DepthSize );
ASSERT_NO_GL_ERROR();
GLint StencilSize;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_STENCIL_SIZE, &StencilSize );
ASSERT_NO_GL_ERROR();
GLint Samples;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &Samples );
ASSERT_NO_GL_ERROR();
FString RenderbufferInfo = FString::Printf( TEXT("\t\t%d x %d, format: %s, samples: %d"), Width, Height, GetGLInternalFormatString( Format ), Samples );
if( RedSize )
{
RenderbufferInfo += FString::Printf( TEXT(", red: %d"), RedSize );
}
if( GreenSize )
{
RenderbufferInfo += FString::Printf( TEXT(", green: %d"), GreenSize );
}
if( BlueSize )
{
RenderbufferInfo += FString::Printf( TEXT(", blue: %d"), BlueSize );
}
if( AlphaSize )
{
RenderbufferInfo += FString::Printf( TEXT(", alpha: %d"), AlphaSize );
}
if( DepthSize )
{
RenderbufferInfo += FString::Printf( TEXT(", depth: %d"), DepthSize );
}
if( StencilSize )
{
RenderbufferInfo += FString::Printf( TEXT(", stencil: %d"), StencilSize );
}
RenderbufferInfo += LINE_TERMINATOR;
LogFile.Log( RenderbufferInfo );
// Restore previous state
if( RenderbufferID != CurrentlyBoundRenderbuffer )
{
glBindRenderbuffer( GL_RENDERBUFFER, CurrentlyBoundRenderbuffer );
}
}
void FOpenGLDebugFrameDumper::DumpFramebufferAttachmentSettings( FOutputDeviceFile& LogFile, GLenum AttachmentSlot )
{
GLint AttachmentType;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType == GL_NONE )
return; // nothing attached; no comment
GLint AttachmentName;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &AttachmentName );
ASSERT_NO_GL_ERROR();
if( AttachmentType == GL_FRAMEBUFFER_DEFAULT )
{
LogFile.Logf( TEXT("\t\tattachment %s is default framebuffer attachment ( name is %d )") LINE_TERMINATOR, GetAttachmentSlotName( AttachmentSlot ), AttachmentName );
}
else if( AttachmentType == GL_TEXTURE )
{
GLint TextureLevel;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &TextureLevel );
ASSERT_NO_GL_ERROR();
GLint CubeMapFace;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, &CubeMapFace );
ASSERT_NO_GL_ERROR();
GLint TextureLayer;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, &TextureLayer );
ASSERT_NO_GL_ERROR();
GLint IsLayered;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_LAYERED, &IsLayered );
ASSERT_NO_GL_ERROR();
GLenum TextureTypeAsk;
GLenum TextureTypeSet;
GLenum TextureTypeFace;
const TCHAR* TextureType;
bool bIsCube = ( CubeMapFace != 0 );
if( bIsCube )
{
TextureTypeAsk = GL_TEXTURE_BINDING_CUBE_MAP;
TextureTypeSet = GL_TEXTURE_CUBE_MAP;
TextureTypeFace = CubeMapFace;
TextureType = TEXT("cube map");
}
else
{
TextureTypeAsk = GL_TEXTURE_BINDING_2D;
TextureTypeSet = GL_TEXTURE_2D;
TextureTypeFace = GL_TEXTURE_2D;
TextureType = TEXT("2D");
}
// Remember what's bound to the texture stage we need to use, to restore it later
GLint BoundTextureID;
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
// Bind our texture. Was it GL_TEXTURE_2D?
ASSERT_NO_GL_ERROR();
GDisableOpenGLDebugOutput = true;
glBindTexture( TextureTypeSet, AttachmentName );
glFinish();
GDisableOpenGLDebugOutput = false;
if( glGetError() )
{
// It could be GL_TEXTURE_2D_MULTISAMPLE then?
check(TextureTypeSet == GL_TEXTURE_2D);
check(TextureLevel == 0);
TextureTypeAsk = GL_TEXTURE_BINDING_2D_MULTISAMPLE;
TextureTypeSet = GL_TEXTURE_2D_MULTISAMPLE;
TextureTypeFace = GL_TEXTURE_2D_MULTISAMPLE;
TextureType = TEXT("2D multisample");
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
ASSERT_NO_GL_ERROR();
GDisableOpenGLDebugOutput = true;
glBindTexture( TextureTypeSet, AttachmentName );
glFinish();
GDisableOpenGLDebugOutput = false;
if( glGetError() )
{
// Ok, then GL_TEXTURE_3D?
TextureTypeAsk = GL_TEXTURE_BINDING_3D;
TextureTypeSet = GL_TEXTURE_3D;
TextureTypeFace = GL_TEXTURE_3D;
TextureType = TEXT("3D");
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
ASSERT_NO_GL_ERROR();
glBindTexture( TextureTypeSet, AttachmentName );
ASSERT_NO_GL_ERROR();
}
}
// Get the information we need
GLint Width;
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_WIDTH, &Width );
ASSERT_NO_GL_ERROR();
GLint Height;
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_HEIGHT, &Height );
ASSERT_NO_GL_ERROR();
GLint Depth;
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_DEPTH, &Depth );
ASSERT_NO_GL_ERROR();
GLint InternalFormat;
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_INTERNAL_FORMAT, &InternalFormat );
ASSERT_NO_GL_ERROR();
// Restore previous binding to this texture stage
if( BoundTextureID != AttachmentName )
{
glBindTexture( TextureTypeSet, BoundTextureID );
ASSERT_NO_GL_ERROR();
}
LogFile.Logf( TEXT("\t\tattachment %s is a %s texture ( ID %d, level %d, %d x %d x %d, %s%s )") LINE_TERMINATOR,
GetAttachmentSlotName( AttachmentSlot ), TextureType, AttachmentName, TextureLevel, Width, Height, Depth,
GetGLInternalFormatString( InternalFormat ), IsLayered ? TEXT(", layered") : TEXT("") );
if( CubeMapFace != 0 && CubeMapFace != GL_TEXTURE_CUBE_MAP )
{
LogFile.Logf( TEXT("\t\t\tcube map face: %s") LINE_TERMINATOR, GetCubeMapFaceName( CubeMapFace ) );
}
}
else if( AttachmentType == GL_RENDERBUFFER )
{
LogFile.Logf( TEXT("\t\tattachment %s is a renderbuffer ( ID %d )") LINE_TERMINATOR, GetAttachmentSlotName( AttachmentSlot ), AttachmentName );
DumpRenderbufferSettings( LogFile, AttachmentName );
}
GLint RedSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &RedSize );
ASSERT_NO_GL_ERROR();
GLint GreenSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &GreenSize );
ASSERT_NO_GL_ERROR();
GLint BlueSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &BlueSize );
ASSERT_NO_GL_ERROR();
GLint AlphaSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &AlphaSize );
ASSERT_NO_GL_ERROR();
GLint DepthSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &DepthSize );
ASSERT_NO_GL_ERROR();
GLint StencilSize;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &StencilSize );
ASSERT_NO_GL_ERROR();
GLint ComponentType;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, &ComponentType );
ASSERT_NO_GL_ERROR();
GLint ColorEncoding;
glGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &ColorEncoding );
ASSERT_NO_GL_ERROR();
{
FString ComponentInfo = FString::Printf( TEXT("\t\t\tComponent type: %s, color encoding: %s"), GetComponentType( ComponentType ), GetColorEncoding( ColorEncoding ) );
if( RedSize )
{
ComponentInfo += FString::Printf( TEXT(", red: %d"), RedSize );
}
if( GreenSize )
{
ComponentInfo += FString::Printf( TEXT(", green: %d"), GreenSize );
}
if( BlueSize )
{
ComponentInfo += FString::Printf( TEXT(", blue: %d"), BlueSize );
}
if( AlphaSize )
{
ComponentInfo += FString::Printf( TEXT(", alpha: %d"), AlphaSize );
}
if( DepthSize )
{
ComponentInfo += FString::Printf( TEXT(", depth: %d"), DepthSize );
}
if( StencilSize )
{
ComponentInfo += FString::Printf( TEXT(", stencil: %d"), StencilSize );
}
ComponentInfo += LINE_TERMINATOR;
LogFile.Log( ComponentInfo );
}
}
void FOpenGLDebugFrameDumper::DumpFramebufferSettings( FOutputDeviceFile& LogFile, GLint FramebufferID )
{
LogFile.Log( TEXT("Framebuffer State") LINE_TERMINATOR );
GLint CurrentlyBoundDrawFramebuffer;
glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &CurrentlyBoundDrawFramebuffer );
ASSERT_NO_GL_ERROR();
if( FramebufferID )
{
GLboolean bIsFramebuffer = glIsFramebuffer( FramebufferID );
ASSERT_NO_GL_ERROR();
if( !bIsFramebuffer )
{
LogFile.Logf( TEXT("\tFramebuffer ID %d is not a valid framebuffer ID! ( %d )") LINE_TERMINATOR, FramebufferID );
return;
}
}
if( FramebufferID != CurrentlyBoundDrawFramebuffer )
{
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, FramebufferID );
ASSERT_NO_GL_ERROR();
}
if( !FramebufferID )
{
LogFile.Log( TEXT("\tFramebuffer object 0 (screen buffer)") LINE_TERMINATOR );
DumpFramebufferAttachmentSettings( LogFile, GL_FRONT_LEFT );
DumpFramebufferAttachmentSettings( LogFile, GL_FRONT_RIGHT );
DumpFramebufferAttachmentSettings( LogFile, GL_BACK_LEFT );
DumpFramebufferAttachmentSettings( LogFile, GL_BACK_RIGHT );
DumpFramebufferAttachmentSettings( LogFile, GL_DEPTH );
DumpFramebufferAttachmentSettings( LogFile, GL_STENCIL );
}
else
{
LogFile.Logf( TEXT("\tFramebuffer object %d info") LINE_TERMINATOR, FramebufferID );
GLint MaxColorAttachments;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &MaxColorAttachments );
ASSERT_NO_GL_ERROR();
for( GLint ColorAttachmentIndex = 0; ColorAttachmentIndex < MaxColorAttachments; ++ColorAttachmentIndex )
{
DumpFramebufferAttachmentSettings( LogFile, GL_COLOR_ATTACHMENT0 + ColorAttachmentIndex );
}
DumpFramebufferAttachmentSettings( LogFile, GL_DEPTH_ATTACHMENT );
DumpFramebufferAttachmentSettings( LogFile, GL_STENCIL_ATTACHMENT );
}
// Restore previous state
if( FramebufferID != CurrentlyBoundDrawFramebuffer )
{
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, CurrentlyBoundDrawFramebuffer );
ASSERT_NO_GL_ERROR();
}
}
void FOpenGLDebugFrameDumper::InterpretUniform( GLint UniformType, void* DataBuffer, FString& OutAppendString )
{
switch( UniformType )
{
case GL_FLOAT:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "%f"), Fields[0] );
}
break;
case GL_FLOAT_VEC2:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %f, %f }"), Fields[0], Fields[1] );
}
break;
case GL_FLOAT_VEC3:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %f, %f, %f }"), Fields[0], Fields[1], Fields[2] );
}
break;
case GL_FLOAT_VEC4:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %f, %f, %f, %f }"), Fields[0], Fields[1], Fields[2], Fields[3] );
}
break;
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_BUFFER:
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
case GL_INT_SAMPLER_1D:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_BUFFER:
case GL_INT_SAMPLER_2D_RECT:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
case GL_INT:
{
int32* Fields = (int32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "%d"), Fields[0] );
}
break;
case GL_INT_VEC2:
{
int32* Fields = (int32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %d, %d }"), Fields[0], Fields[1] );
}
break;
case GL_INT_VEC3:
{
int32* Fields = (int32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %d, %d, %d }"), Fields[0], Fields[1], Fields[2] );
}
break;
case GL_INT_VEC4:
{
int32* Fields = (int32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %d, %d, %d, %d }"), Fields[0], Fields[1], Fields[2], Fields[3] );
}
break;
case GL_UNSIGNED_INT:
{
uint32* Fields = (uint32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "%u"), Fields[0] );
}
break;
case GL_UNSIGNED_INT_VEC2:
{
uint32* Fields = (uint32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %u, %u }"), Fields[0], Fields[1] );
}
break;
case GL_UNSIGNED_INT_VEC3:
{
uint32* Fields = (uint32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %u, %u, %u }"), Fields[0], Fields[1], Fields[2] );
}
break;
case GL_UNSIGNED_INT_VEC4:
{
uint32* Fields = (uint32*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %u, %u, %u, %u }"), Fields[0], Fields[1], Fields[2], Fields[3] );
}
break;
case GL_BOOL:
{
GLboolean* Fields = (GLboolean*)DataBuffer;
OutAppendString += Fields[0] ? TEXT("TRUE") : TEXT("FALSE");
}
break;
case GL_BOOL_VEC2:
{
GLboolean* Fields = (GLboolean*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %s, %s }"),
Fields[0] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[1] ? TEXT("TRUE") : TEXT("FALSE")
);
}
break;
case GL_BOOL_VEC3:
{
GLboolean* Fields = (GLboolean*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %s, %s, %s }"),
Fields[0] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[1] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[2] ? TEXT("TRUE") : TEXT("FALSE")
);
}
break;
case GL_BOOL_VEC4:
{
GLboolean* Fields = (GLboolean*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ %s, %s, %s, %s }"),
Fields[0] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[1] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[2] ? TEXT("TRUE") : TEXT("FALSE"),
Fields[3] ? TEXT("TRUE") : TEXT("FALSE")
);
}
break;
case GL_FLOAT_MAT2:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f }, { %f, %f } }"), Fields[0], Fields[1], Fields[2], Fields[3] );
}
break;
case GL_FLOAT_MAT3:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f }, { %f, %f, %f }, { %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7], Fields[8] );
}
break;
case GL_FLOAT_MAT4:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f, %f }, { %f, %f, %f, %f }, { %f, %f, %f, %f }, { %f, %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7],
Fields[8], Fields[9], Fields[10], Fields[11], Fields[12], Fields[13], Fields[14], Fields[15]
);
}
break;
case GL_FLOAT_MAT2x3:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f }, { %f, %f }, { %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5] );
}
break;
case GL_FLOAT_MAT2x4:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f }, { %f, %f }, { %f, %f }, { %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7] );
}
break;
case GL_FLOAT_MAT3x2:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f }, { %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5] );
}
break;
case GL_FLOAT_MAT3x4:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f }, { %f, %f, %f }, { %f, %f, %f }, { %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7],
Fields[8], Fields[9], Fields[10], Fields[11]
);
}
break;
case GL_FLOAT_MAT4x2:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f, %f }, { %f, %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7] );
}
break;
case GL_FLOAT_MAT4x3:
{
float* Fields = (float*)DataBuffer;
OutAppendString += FString::Printf( TEXT( "{ { %f, %f, %f, %f }, { %f, %f, %f, %f }, { %f, %f, %f, %f } }"),
Fields[0], Fields[1], Fields[2], Fields[3], Fields[4], Fields[5], Fields[6], Fields[7],
Fields[8], Fields[9], Fields[10], Fields[11]
);
}
break;
default:
OutAppendString += TEXT("!!!unknown!!!");
break;
};
}
void FOpenGLDebugFrameDumper::DumpProgramContents( FOutputDeviceFile& LogFile, GLint ProgramID )
{
GLboolean bIsProgram = glIsProgram( ProgramID );
ASSERT_NO_GL_ERROR();
if( !bIsProgram )
{
LogFile.Logf( TEXT("Program ID %d is not a valid program ID!") LINE_TERMINATOR, ProgramID );
return;
}
LogFile.Logf( TEXT("Program %d info") LINE_TERMINATOR, ProgramID );
{
GLint Status;
glGetProgramiv( ProgramID, GL_DELETE_STATUS, &Status );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DELETE_STATUS: %s") LINE_TERMINATOR, Status ? TEXT("TRUE") : TEXT("FALSE") );
}
{
GLint Status;
glGetProgramiv( ProgramID, GL_LINK_STATUS, &Status );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_LINK_STATUS: %s") LINE_TERMINATOR, Status ? TEXT("TRUE") : TEXT("FALSE") );
}
{
GLint Status;
glGetProgramiv( ProgramID, GL_VALIDATE_STATUS, &Status );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_VALIDATE_STATUS: %s") LINE_TERMINATOR, Status ? TEXT("TRUE") : TEXT("FALSE") );
}
GLint AttachedShaderCount;
glGetProgramiv( ProgramID, GL_ATTACHED_SHADERS, &AttachedShaderCount );
ASSERT_NO_GL_ERROR();
if( AttachedShaderCount )
{
GLsizei CountReceived = 0;
GLuint* AttachedShadersTable = (GLuint*)FMemory::Malloc( sizeof(GLuint)*AttachedShaderCount );
glGetAttachedShaders( ProgramID, AttachedShaderCount, &CountReceived, AttachedShadersTable );
ASSERT_NO_GL_ERROR();
FString ShaderNumbers = TEXT("");
for( GLint ShaderIndex = 0; ShaderIndex < CountReceived; ++ShaderIndex )
{
ShaderNumbers += FString::Printf( TEXT("%s%u"), ShaderIndex ? TEXT(", ") : TEXT(""), AttachedShadersTable[ShaderIndex] );
}
LogFile.Logf( TEXT("\tAttached shaders: %d ( %s )") LINE_TERMINATOR, AttachedShaderCount, *ShaderNumbers );
FMemory::Free( AttachedShadersTable );
}
// Attributes
GLint ActiveAttributesCount;
glGetProgramiv( ProgramID, GL_ACTIVE_ATTRIBUTES, &ActiveAttributesCount );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tActive attributes: %d") LINE_TERMINATOR, ActiveAttributesCount );
if( ActiveAttributesCount )
{
GLint MaxActiveAttributeNameLength;
glGetProgramiv( ProgramID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &MaxActiveAttributeNameLength );
ASSERT_NO_GL_ERROR();
if( MaxActiveAttributeNameLength )
{
ANSICHAR* ActiveAttributeName = (ANSICHAR*)FMemory::Malloc( sizeof(ANSICHAR)*(MaxActiveAttributeNameLength+1) );
ActiveAttributeName[MaxActiveAttributeNameLength] = 0;
for( GLint AttributeIndex = 0; AttributeIndex < ActiveAttributesCount; ++AttributeIndex )
{
GLsizei NameLength = 0;
GLint Size = 0;
GLenum Type = 0;
glGetActiveAttrib( ProgramID, AttributeIndex, MaxActiveAttributeNameLength+1, &NameLength, &Size, &Type, ActiveAttributeName );
ASSERT_NO_GL_ERROR();
GLint AttributeLocation = glGetAttribLocation( ProgramID, ActiveAttributeName );
ASSERT_NO_GL_ERROR();
FString ActiveAttributeNameString( ActiveAttributeName );
LogFile.Logf( TEXT("\t%04d: %s ( type %s, location %d, size %d )") LINE_TERMINATOR, AttributeIndex, *ActiveAttributeNameString, GetGLUniformTypeString( Type ), AttributeLocation, Size );
}
FMemory::Free( ActiveAttributeName );
}
}
// Uniforms
GLint ActiveUniformCount;
glGetProgramiv( ProgramID, GL_ACTIVE_UNIFORMS, &ActiveUniformCount );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tActive uniforms: %d") LINE_TERMINATOR, ActiveUniformCount );
if( ActiveUniformCount )
{
GLint MaxActiveUniformNameLength;
glGetProgramiv( ProgramID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &MaxActiveUniformNameLength );
ASSERT_NO_GL_ERROR();
if( MaxActiveUniformNameLength )
{
ANSICHAR* ActiveUniformName = (ANSICHAR*)FMemory::Malloc( sizeof(ANSICHAR)*(MaxActiveUniformNameLength+1) );
ActiveUniformName[MaxActiveUniformNameLength] = 0;
for( GLint ActiveUniformIndex = 0; ActiveUniformIndex < ActiveUniformCount; ++ActiveUniformIndex )
{
GLsizei NameLengthReceived;
glGetActiveUniformName( ProgramID, ActiveUniformIndex, MaxActiveUniformNameLength+1, &NameLengthReceived, ActiveUniformName );
ASSERT_NO_GL_ERROR();
GLuint TempUniformIndex = ActiveUniformIndex;
GLint UniformType;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_TYPE, &UniformType );
ASSERT_NO_GL_ERROR();
GLint UniformSize;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_SIZE, &UniformSize );
ASSERT_NO_GL_ERROR();
GLint UniformIsRowMajor;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_IS_ROW_MAJOR, &UniformIsRowMajor );
ASSERT_NO_GL_ERROR();
FString ActiveUniformNameString( ActiveUniformName );
LogFile.Logf( TEXT("\t%04d: %s ( type %s )") LINE_TERMINATOR, ActiveUniformIndex, *ActiveUniformNameString, GetGLUniformTypeString( UniformType ) );
GLint UniformBlockIndex;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_BLOCK_INDEX, &UniformBlockIndex );
ASSERT_NO_GL_ERROR();
if( UniformBlockIndex == -1 ) // default uniform
{
GLint UniformOffset = glGetUniformLocation( ProgramID, ActiveUniformName );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t ( size %d, DEFAULT uniform block : location %d, is row major %d )") LINE_TERMINATOR,
UniformSize, UniformOffset, UniformIsRowMajor );
for( int32 UniformArrayMemberIndex = 0; UniformArrayMemberIndex < UniformSize; ++UniformArrayMemberIndex )
{
float DataBuffer[16]; // so it can fit all types
switch( UniformType )
{
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
glGetUniformfv( ProgramID, UniformOffset, DataBuffer );
ASSERT_NO_GL_ERROR();
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
glGetUniformuiv( ProgramID, UniformOffset, (GLuint*)DataBuffer );
ASSERT_NO_GL_ERROR();
break;
default:
glGetUniformiv( ProgramID, UniformOffset, (GLint*)DataBuffer );
ASSERT_NO_GL_ERROR();
break;
}
FString Line = TEXT("\t ");
InterpretUniform( UniformType, DataBuffer, Line );
Line += LINE_TERMINATOR;
LogFile.Log( Line );
++UniformOffset;
}
}
else
{
GLint UniformOffset;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_OFFSET, &UniformOffset );
ASSERT_NO_GL_ERROR();
GLint UniformArrayStride;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_ARRAY_STRIDE, &UniformArrayStride );
ASSERT_NO_GL_ERROR();
GLint UniformMatrixStride;
glGetActiveUniformsiv( ProgramID, 1, &TempUniformIndex, GL_UNIFORM_MATRIX_STRIDE, &UniformMatrixStride );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t ( size %d, uniform block %d : offset %d array stride %d, matrix stride %d, is row major %d )") LINE_TERMINATOR,
UniformSize, UniformBlockIndex, UniformOffset, UniformArrayStride, UniformMatrixStride, UniformIsRowMajor );
GLint UniformBlockBinding;
glGetActiveUniformBlockiv( ProgramID, UniformBlockIndex, GL_UNIFORM_BLOCK_BINDING, &UniformBlockBinding );
ASSERT_NO_GL_ERROR();
GLint UniformBufferID;
glGetIntegeri_v( GL_UNIFORM_BUFFER_BINDING, UniformBlockBinding, &UniformBufferID );
ASSERT_NO_GL_ERROR();
if( UniformBufferID )
{
GLint CurrentUniformBufferBinding;
glGetIntegerv( GL_UNIFORM_BUFFER_BINDING, &CurrentUniformBufferBinding );
ASSERT_NO_GL_ERROR();
glBindBuffer( GL_UNIFORM_BUFFER, UniformBufferID );
ASSERT_NO_GL_ERROR();
GLint64 TotalBufferSize = 0LL; // getters apparently like to overwrite just some bytes of this on Mac, if they return small number, so we need to put 0 in both dwords initially
glGetBufferParameteri64v( GL_UNIFORM_BUFFER, GL_BUFFER_SIZE, &TotalBufferSize );
ASSERT_NO_GL_ERROR();
GLuint BufferSize = (GLuint)TotalBufferSize; // yes, I know it's conversion to shorter variable, I don't expect buffers > 4GB in size.
int32 SizeToMap = UniformArrayStride ? UniformSize*UniformArrayStride : 16*sizeof(float);
int32 MaxSizeToMap = BufferSize-UniformOffset;
if( MaxSizeToMap < SizeToMap )
{
SizeToMap = MaxSizeToMap;
}
if( SizeToMap <= 0 )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Active uniform is beyond the end of the uniform buffer!") );
}
else
{
void* BufferPtr = glMapBufferRange( GL_UNIFORM_BUFFER, UniformOffset, SizeToMap, GL_MAP_READ_BIT );
ASSERT_NO_GL_ERROR();
if( BufferPtr )
{
for( int32 UniformArrayMemberIndex = 0; UniformArrayMemberIndex < UniformSize; ++UniformArrayMemberIndex )
{
uint8* DataBuffer = (uint8*)BufferPtr + UniformArrayMemberIndex * UniformArrayStride;
FString Line = TEXT("\t ");
InterpretUniform( UniformType, DataBuffer, Line );
Line += LINE_TERMINATOR;
LogFile.Log( Line );
}
glUnmapBuffer( GL_UNIFORM_BUFFER );
ASSERT_NO_GL_ERROR();
}
else
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Failed to map uniform buffer %d!"), UniformBlockBinding );
}
}
glBindBuffer( GL_UNIFORM_BUFFER, CurrentUniformBufferBinding );
ASSERT_NO_GL_ERROR();
}
else
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Nothing bound to active uniform block right after draw!") );
}
}
}
FMemory::Free( ActiveUniformName );
}
}
// Uniform blocks
GLint ActiveUniformBlockCount;
glGetProgramiv( ProgramID, GL_ACTIVE_UNIFORM_BLOCKS, &ActiveUniformBlockCount );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tActive uniform blocks: %d") LINE_TERMINATOR, ActiveUniformBlockCount );
if( ActiveUniformBlockCount )
{
GLint MaxActiveUniformBlockNameLength;
glGetProgramiv( ProgramID, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &MaxActiveUniformBlockNameLength );
ASSERT_NO_GL_ERROR();
if( MaxActiveUniformBlockNameLength )
{
ANSICHAR* ActiveUniformBlockName = (ANSICHAR*)FMemory::Malloc( sizeof(ANSICHAR)*(MaxActiveUniformBlockNameLength+1) );
ActiveUniformBlockName[MaxActiveUniformBlockNameLength] = 0;
for( GLint ActiveUniformBlockIndex = 0; ActiveUniformBlockIndex < ActiveUniformBlockCount; ++ActiveUniformBlockIndex )
{
GLsizei NameLengthReceived;
glGetActiveUniformBlockName( ProgramID, ActiveUniformBlockIndex, MaxActiveUniformBlockNameLength+1, &NameLengthReceived, ActiveUniformBlockName );
ASSERT_NO_GL_ERROR();
GLint UniformBlockBinding;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_BINDING, &UniformBlockBinding );
ASSERT_NO_GL_ERROR();
GLint UniformBlockDataSize;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &UniformBlockDataSize );
ASSERT_NO_GL_ERROR();
GLint ActiveUniformsInBlockCount;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ActiveUniformsInBlockCount );
ASSERT_NO_GL_ERROR();
GLint IsReferencedByVertexShader;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, &IsReferencedByVertexShader );
ASSERT_NO_GL_ERROR();
GLint IsReferencedByGeometryShader;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, &IsReferencedByGeometryShader );
ASSERT_NO_GL_ERROR();
GLint IsReferencedByFragmentShader;
glGetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, &IsReferencedByFragmentShader );
ASSERT_NO_GL_ERROR();
// TO DO someday, if ever needed - get a list of active uniform indices for the block
// gl3GetActiveUniformBlockiv( ProgramID, ActiveUniformBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &TableOfIndices );
FString ActiveUniformBlockNameString( ActiveUniformBlockName );
LogFile.Logf( TEXT("\t%02d: %s ( binding %d, size %d, active uniforms %d, referenced by: %s%s%s )") LINE_TERMINATOR,
ActiveUniformBlockIndex,
*ActiveUniformBlockNameString,
UniformBlockBinding,
UniformBlockDataSize,
ActiveUniformsInBlockCount,
IsReferencedByVertexShader ? TEXT("V") : TEXT("_"),
IsReferencedByGeometryShader ? TEXT("G") : TEXT("_"),
IsReferencedByFragmentShader ? TEXT("F") : TEXT("_")
);
}
FMemory::Free( ActiveUniformBlockName );
}
}
// glValidateProgram
glValidateProgram( ProgramID );
GLint ValidationStatus;
glGetProgramiv( ProgramID, GL_VALIDATE_STATUS, &ValidationStatus );
LogFile.Logf( TEXT("\tProgram validation status: %s") LINE_TERMINATOR, ( ValidationStatus == GL_FALSE ) ? TEXT("FALSE") : TEXT("TRUE") );
// At the end, info log
GLint InfoLogLength;
glGetProgramiv( ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength );
ASSERT_NO_GL_ERROR();
if( InfoLogLength )
{
ANSICHAR* ANSIBuffer = (ANSICHAR*)FMemory::Malloc( sizeof(ANSICHAR)*InfoLogLength ); // OpenGL gives back char*
glGetProgramInfoLog( ProgramID, InfoLogLength, NULL, ANSIBuffer );
FString Buffer( ANSIBuffer );
LogFile.Logf( TEXT("\tProgram info log:") LINE_TERMINATOR
TEXT("==============================================") LINE_TERMINATOR
TEXT("%s==============================================") LINE_TERMINATOR,
*Buffer );
FMemory::Free( ANSIBuffer );
}
else
{
LogFile.Log( TEXT("\tNo program info log") LINE_TERMINATOR );
}
// TO DO someday - add geometry shader and transform feedback information here. Skipping it for now.
}
void FOpenGLDebugFrameDumper::DumpShaderContents( FOutputDeviceFile& LogFile, GLint ShaderID )
{
GLboolean bIsShader = glIsShader( ShaderID );
ASSERT_NO_GL_ERROR();
if( !bIsShader )
{
LogFile.Logf( TEXT("Shader ID %d is not a valid shader ID!") LINE_TERMINATOR, ShaderID );
return;
}
LogFile.Logf( TEXT("Shader %d info") LINE_TERMINATOR, ShaderID );
GLint ShaderType;
glGetShaderiv( ShaderID, GL_SHADER_TYPE, &ShaderType );
ASSERT_NO_GL_ERROR();
const TCHAR* ShaderTypeString = GetShaderType( ShaderType );
LogFile.Logf( TEXT("\tGL_SHADER_TYPE: %s") LINE_TERMINATOR, ShaderTypeString );
{
GLint Status;
glGetShaderiv( ShaderID, GL_DELETE_STATUS, &Status );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_DELETE_STATUS: %s") LINE_TERMINATOR, Status ? TEXT("TRUE") : TEXT("FALSE") );
}
{
GLint Status;
glGetShaderiv( ShaderID, GL_COMPILE_STATUS, &Status );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_COMPILE_STATUS: %s") LINE_TERMINATOR, Status ? TEXT("TRUE") : TEXT("FALSE") );
}
GLint ShaderSourceLength = 0;
glGetShaderiv( ShaderID, GL_SHADER_SOURCE_LENGTH, &ShaderSourceLength );
ASSERT_NO_GL_ERROR();
if( ShaderSourceLength )
{
ANSICHAR* SourceCode = (ANSICHAR*)FMemory::Malloc( ShaderSourceLength );
glGetShaderSource( ShaderID, ShaderSourceLength, 0, SourceCode );
ASSERT_NO_GL_ERROR();
uint32 CRC = FCrc::MemCrc_DEPRECATED( SourceCode, ShaderSourceLength );
LogFile.Logf( TEXT("\tShader source code (length %u characters, CRC: 0x%x):") LINE_TERMINATOR
TEXT("==============================================") LINE_TERMINATOR
TEXT("%s==============================================") LINE_TERMINATOR,
ShaderSourceLength-1, CRC, *FString( SourceCode ) );
FMemory::Free( SourceCode );
}
else
{
LogFile.Logf( TEXT("\tNo shader source code") LINE_TERMINATOR );
}
GLint ShaderInfoLogLength = 0;
glGetShaderiv( ShaderID, GL_INFO_LOG_LENGTH, &ShaderInfoLogLength );
ASSERT_NO_GL_ERROR();
if( ShaderInfoLogLength )
{
ANSICHAR* InfoLog = (ANSICHAR*)FMemory::Malloc( ShaderInfoLogLength+1 );
glGetShaderInfoLog( ShaderID, ShaderInfoLogLength+1, 0, InfoLog );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tShader info log:") LINE_TERMINATOR
TEXT("==============================================") LINE_TERMINATOR
TEXT("%s==============================================") LINE_TERMINATOR,
*FString( InfoLog ) );
FMemory::Free( InfoLog );
}
else
{
LogFile.Logf( TEXT("\tNo shader info log") LINE_TERMINATOR );
}
}
void FOpenGLDebugFrameDumper::GetBoundTextureSurfaceLevelSettings( GLenum SurfaceType, GLint Level, TextureLevelInfo& OutInfo )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_WIDTH, &OutInfo.Width );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_HEIGHT, &OutInfo.Height );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_DEPTH, &OutInfo.Depth );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_SAMPLES, &OutInfo.Samples );
ASSERT_NO_GL_ERROR();
GLint FixedSampleLocations;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_FIXED_SAMPLE_LOCATIONS, &FixedSampleLocations );
ASSERT_NO_GL_ERROR();
OutInfo.bHasFixedSampleLocations = ( FixedSampleLocations != 0 );
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_INTERNAL_FORMAT, &OutInfo.InternalFormat );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_RED_SIZE, &OutInfo.RedBits );
ASSERT_NO_GL_ERROR();
if( OutInfo.RedBits )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_RED_TYPE, &OutInfo.RedType );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.RedType = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_GREEN_SIZE, &OutInfo.GreenBits );
ASSERT_NO_GL_ERROR();
if( OutInfo.GreenBits )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_GREEN_TYPE, &OutInfo.GreenType );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.GreenType = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_BLUE_SIZE, &OutInfo.BlueBits );
ASSERT_NO_GL_ERROR();
if( OutInfo.BlueBits )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_BLUE_TYPE, &OutInfo.BlueType );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.BlueType = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_ALPHA_SIZE, &OutInfo.AlphaBits );
ASSERT_NO_GL_ERROR();
if( OutInfo.AlphaBits )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_ALPHA_TYPE, &OutInfo.AlphaType );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.AlphaType = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_DEPTH_SIZE, &OutInfo.DepthBits );
ASSERT_NO_GL_ERROR();
if( OutInfo.DepthBits )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_DEPTH_TYPE, &OutInfo.DepthType );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.DepthType = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_STENCIL_SIZE, &OutInfo.StencilBits );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_SHARED_SIZE, &OutInfo.SharedSize );
ASSERT_NO_GL_ERROR();
GLint TextureCompressed;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_COMPRESSED, &TextureCompressed );
ASSERT_NO_GL_ERROR();
OutInfo.bIsCompressed = ( TextureCompressed != 0 );
if( OutInfo.bIsCompressed )
{
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &OutInfo.CompressedSize );
ASSERT_NO_GL_ERROR();
}
else
{
OutInfo.CompressedSize = 0;
}
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_BUFFER_DATA_STORE_BINDING, &OutInfo.DataStoreBinding );
ASSERT_NO_GL_ERROR();
}
void FOpenGLDebugFrameDumper::DumpBoundTextureSurfaceSettings( FOutputDeviceFile& LogFile, GLenum SurfaceType, GLint BaseLevel, GLint MaxLevel )
{
TextureLevelInfo BaseInfo;
GetBoundTextureSurfaceLevelSettings( SurfaceType, BaseLevel, BaseInfo );
// Log base level
LogFile.Logf( TEXT("\tBase level ( %d ) info") LINE_TERMINATOR, BaseLevel );
LogFile.Logf( TEXT("\t\t%d x %d x %d ( %d samples, shared size %d )") LINE_TERMINATOR, BaseInfo.Width, BaseInfo.Height, BaseInfo.Depth, BaseInfo.Samples, BaseInfo.SharedSize );
if( BaseInfo.bHasFixedSampleLocations )
{
LogFile.Log( TEXT("\t\tfixed sample locations") LINE_TERMINATOR );
}
LogFile.Logf( TEXT("\t\tInternal format: %s") LINE_TERMINATOR, GetGLInternalFormatString( BaseInfo.InternalFormat ) );
if( BaseInfo.RedBits )
{
LogFile.Logf( TEXT("\t\tR bits: %d, component type: %s") LINE_TERMINATOR, BaseInfo.RedBits, GetComponentType( BaseInfo.RedType ) );
}
if( BaseInfo.GreenBits )
{
LogFile.Logf( TEXT("\t\tG bits: %d, component type: %s") LINE_TERMINATOR, BaseInfo.GreenBits, GetComponentType( BaseInfo.GreenType ) );
}
if( BaseInfo.BlueBits )
{
LogFile.Logf( TEXT("\t\tB bits: %d, component type: %s") LINE_TERMINATOR, BaseInfo.BlueBits, GetComponentType( BaseInfo.BlueType ) );
}
if( BaseInfo.AlphaBits )
{
LogFile.Logf( TEXT("\t\tA bits: %d, component type: %s") LINE_TERMINATOR, BaseInfo.AlphaBits, GetComponentType( BaseInfo.AlphaType ) );
}
if( BaseInfo.DepthBits )
{
LogFile.Logf( TEXT("\t\tDepth bits: %d, component type: %s") LINE_TERMINATOR, BaseInfo.DepthBits, GetComponentType( BaseInfo.DepthType ) );
}
if( BaseInfo.StencilBits )
{
LogFile.Logf( TEXT("\t\tStencil bits: %d") LINE_TERMINATOR, BaseInfo.StencilBits );
}
if( BaseInfo.bIsCompressed )
{
LogFile.Logf( TEXT("\t\tTexture compressed, size: %d") LINE_TERMINATOR, BaseInfo.CompressedSize );
}
if( BaseInfo.DataStoreBinding )
{
LogFile.Logf( TEXT("\t\tData store binding: %d") LINE_TERMINATOR, BaseInfo.DataStoreBinding );
}
if( MaxLevel > BaseLevel )
LogFile.Log( TEXT("\t") LINE_TERMINATOR );
TextureLevelInfo PreviousLevelInfo = BaseInfo;
for( GLint Level = BaseLevel+1; Level <= MaxLevel; ++Level )
{
TextureLevelInfo NewInfo;
GetBoundTextureSurfaceLevelSettings( SurfaceType, Level, NewInfo );
LogFile.Logf( TEXT("\tLevel %d: %d x %d x %d ( %d samples, shared size: %d )") LINE_TERMINATOR, Level, NewInfo.Width, NewInfo.Height, NewInfo.Depth, NewInfo.Samples, NewInfo.SharedSize );
if( NewInfo.bHasFixedSampleLocations != PreviousLevelInfo.bHasFixedSampleLocations )
{
LogFile.Logf( TEXT("\t\tfixed sample locations: %s") LINE_TERMINATOR, NewInfo.bHasFixedSampleLocations ? TEXT("TRUE") : TEXT("FALSE") );
}
if( NewInfo.InternalFormat != PreviousLevelInfo.InternalFormat )
{
LogFile.Logf( TEXT("\t\tInternal format: %s") LINE_TERMINATOR, GetGLInternalFormatString( NewInfo.InternalFormat ) );
}
if( NewInfo.RedBits != PreviousLevelInfo.RedBits )
{
if( NewInfo.RedBits )
{
LogFile.Logf( TEXT("\t\tR bits: %d, component type: %s") LINE_TERMINATOR, NewInfo.RedBits, GetComponentType( NewInfo.RedType ) );
}
else
{
LogFile.Log( TEXT("\t\tR bits gone!") );
}
}
if( NewInfo.GreenBits != PreviousLevelInfo.GreenBits )
{
if( NewInfo.GreenBits )
{
LogFile.Logf( TEXT("\t\tG bits: %d, component type: %s") LINE_TERMINATOR, NewInfo.GreenBits, GetComponentType( NewInfo.GreenType ) );
}
else
{
LogFile.Log( TEXT("\t\tG bits gone!") );
}
}
if( NewInfo.BlueBits != PreviousLevelInfo.BlueBits )
{
if( NewInfo.BlueBits )
{
LogFile.Logf( TEXT("\t\tB bits: %d, component type: %s") LINE_TERMINATOR, NewInfo.BlueBits, GetComponentType( NewInfo.BlueType ) );
}
else
{
LogFile.Log( TEXT("\t\tB bits gone!") );
}
}
if( NewInfo.AlphaBits != PreviousLevelInfo.AlphaBits )
{
if( NewInfo.AlphaBits )
{
LogFile.Logf( TEXT("\t\tA bits: %d, component type: %s") LINE_TERMINATOR, NewInfo.AlphaBits, GetComponentType( NewInfo.AlphaType ) );
}
else
{
LogFile.Log( TEXT("\t\tA bits gone!") );
}
}
if( NewInfo.DepthBits != PreviousLevelInfo.DepthBits )
{
if( NewInfo.DepthBits )
{
LogFile.Logf( TEXT("\t\tDepth bits: %d, component type: %s") LINE_TERMINATOR, NewInfo.DepthBits, GetComponentType( NewInfo.DepthType ) );
}
else
{
LogFile.Log( TEXT("\t\tDepth bits gone!") );
}
}
if( NewInfo.StencilBits != PreviousLevelInfo.StencilBits )
{
if( NewInfo.StencilBits )
{
LogFile.Logf( TEXT("\t\tStencil bits: %d") LINE_TERMINATOR, NewInfo.StencilBits );
}
else
{
LogFile.Log( TEXT("\t\tStencil bits gone!") );
}
}
if( NewInfo.bIsCompressed != PreviousLevelInfo.bIsCompressed )
{
if( NewInfo.bIsCompressed )
{
LogFile.Logf( TEXT("\t\tTexture compressed, size: %d") LINE_TERMINATOR, NewInfo.CompressedSize );
}
else
{
LogFile.Log( TEXT("\t\tTexture not compressed now!") LINE_TERMINATOR );
}
}
if( NewInfo.DataStoreBinding )
{
LogFile.Logf( TEXT("\t\tData store binding: %d") LINE_TERMINATOR, NewInfo.DataStoreBinding );
}
PreviousLevelInfo = NewInfo;
}
LogFile.Log( TEXT("\t") LINE_TERMINATOR );
}
void FOpenGLDebugFrameDumper::DumpBoundTextureSettings( FOutputDeviceFile& LogFile, GLenum UnitTarget )
{
GLfloat BorderColor[4];
glGetTexParameterfv( UnitTarget, GL_TEXTURE_BORDER_COLOR, BorderColor );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_BORDER_COLOR: ( %f, %f, %f, %f )") LINE_TERMINATOR, BorderColor[0], BorderColor[1], BorderColor[2], BorderColor[3] );
GLint TextureMinFilter;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_MIN_FILTER, &TextureMinFilter );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MIN_FILTER: %s") LINE_TERMINATOR, GetGLTextureFilterString( TextureMinFilter ) );
GLint TextureMagFilter;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_MAG_FILTER, &TextureMagFilter );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MAG_FILTER: %s") LINE_TERMINATOR, GetGLTextureFilterString( TextureMagFilter ) );
GLint TextureWrapS;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_WRAP_S, &TextureWrapS );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_S: %s") LINE_TERMINATOR, GetGLTextureWrapString( TextureWrapS ) );
GLint TextureWrapT;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_WRAP_T, &TextureWrapT );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_T: %s") LINE_TERMINATOR, GetGLTextureWrapString( TextureWrapT ) );
GLint TextureWrapR;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_WRAP_R, &TextureWrapR );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_R: %s") LINE_TERMINATOR, GetGLTextureWrapString( TextureWrapR ) );
GLfloat TextureMinLOD;
glGetTexParameterfv( UnitTarget, GL_TEXTURE_MIN_LOD, &TextureMinLOD );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MIN_LOD: %f") LINE_TERMINATOR, TextureMinLOD );
GLfloat TextureMaxLOD;
glGetTexParameterfv( UnitTarget, GL_TEXTURE_MAX_LOD, &TextureMaxLOD );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MAX_LOD: %f") LINE_TERMINATOR, TextureMaxLOD );
GLint TextureBaseLevel;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_BASE_LEVEL, &TextureBaseLevel );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_BASE_LEVEL: %d") LINE_TERMINATOR, TextureBaseLevel );
GLint TextureMaxLevel;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_MAX_LEVEL, &TextureMaxLevel );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MAX_LEVEL: %d") LINE_TERMINATOR, TextureMaxLevel );
GLfloat TextureLODBias;
glGetTexParameterfv( UnitTarget, GL_TEXTURE_LOD_BIAS, &TextureLODBias );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_LOD_BIAS: %f") LINE_TERMINATOR, TextureLODBias );
GLint TextureCompareMode;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_COMPARE_MODE, &TextureCompareMode );
ASSERT_NO_GL_ERROR();
if( TextureCompareMode != GL_NONE )
{
LogFile.Logf( TEXT("\tGL_TEXTURE_COMPARE_MODE: unknown value ( 0x%x )") LINE_TERMINATOR, TextureCompareMode );
GLint TextureCompareFunc;
glGetTexParameteriv( UnitTarget, GL_TEXTURE_COMPARE_FUNC, &TextureCompareFunc );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_TEXTURE_COMPARE_FUNC: %s") LINE_TERMINATOR, GetGLCompareString( TextureCompareFunc ) );
}
else
{
LogFile.Log( TEXT("\tGL_TEXTURE_COMPARE_MODE: GL_NONE") LINE_TERMINATOR );
}
LogFile.Log( TEXT("\t") LINE_TERMINATOR );
if( TextureBaseLevel > TextureMaxLevel )
{
LogFile.Logf( TEXT("\tBase texture level > max level, data makes no sense!") LINE_TERMINATOR TEXT("\t") LINE_TERMINATOR );
}
else if( UnitTarget == GL_TEXTURE_CUBE_MAP )
{
for( GLint TextureFace = GL_TEXTURE_CUBE_MAP_POSITIVE_X; TextureFace <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; ++TextureFace )
{
LogFile.Logf( TEXT("\tTexture face: %s") LINE_TERMINATOR TEXT("\t") LINE_TERMINATOR, GetCubeMapFaceName( TextureFace ) );
DumpBoundTextureSurfaceSettings( LogFile, TextureFace, TextureBaseLevel, TextureMaxLevel );
}
}
else
{
DumpBoundTextureSurfaceSettings( LogFile, UnitTarget, TextureBaseLevel, TextureMaxLevel );
}
}
void FOpenGLDebugFrameDumper::DumpBoundSamplerSettings( FOutputDeviceFile& LogFile, GLint SamplerID )
{
GLfloat BorderColor[4];
glGetSamplerParameterfv( SamplerID, GL_TEXTURE_BORDER_COLOR, BorderColor );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_BORDER_COLOR: ( %f, %f, %f, %f )") LINE_TERMINATOR, BorderColor[0], BorderColor[1], BorderColor[2], BorderColor[3] );
GLint MinFilter;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_MIN_FILTER, &MinFilter );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MIN_FILTER: %s") LINE_TERMINATOR, GetGLTextureFilterString( MinFilter ) );
GLint MagFilter;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_MAG_FILTER, &MagFilter );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MAG_FILTER: %s") LINE_TERMINATOR, GetGLTextureFilterString( MagFilter ) );
GLint WrapS;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_WRAP_S, &WrapS );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_S: %s") LINE_TERMINATOR, GetGLTextureWrapString( WrapS ) );
GLint WrapT;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_WRAP_T, &WrapT );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_T: %s") LINE_TERMINATOR, GetGLTextureWrapString( WrapT ) );
GLint WrapR;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_WRAP_R, &WrapR );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_WRAP_R: %s") LINE_TERMINATOR, GetGLTextureWrapString( WrapR ) );
GLfloat MinLOD;
glGetSamplerParameterfv( SamplerID, GL_TEXTURE_MIN_LOD, &MinLOD );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MIN_LOD: %f") LINE_TERMINATOR, MinLOD );
GLfloat MaxLOD;
glGetSamplerParameterfv( SamplerID, GL_TEXTURE_MAX_LOD, &MaxLOD );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_MAX_LOD: %f") LINE_TERMINATOR, MaxLOD );
GLfloat LODBias;
glGetSamplerParameterfv( SamplerID, GL_TEXTURE_LOD_BIAS, &LODBias );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\tGL_TEXTURE_LOD_BIAS: %f") LINE_TERMINATOR, LODBias );
GLint CompareMode;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_COMPARE_MODE, &CompareMode );
ASSERT_NO_GL_ERROR();
if( CompareMode != GL_NONE )
{
LogFile.Logf( TEXT("\tGL_TEXTURE_COMPARE_MODE: unknown value ( 0x%x )") LINE_TERMINATOR, CompareMode );
GLint CompareFunc;
glGetSamplerParameteriv( SamplerID, GL_TEXTURE_COMPARE_FUNC, &CompareFunc );
ASSERT_NO_GL_ERROR();
LogFile.Logf( TEXT("\t\tGL_TEXTURE_COMPARE_FUNC: %s") LINE_TERMINATOR, GetGLCompareString( CompareFunc ) );
}
else
{
LogFile.Log( TEXT("\tGL_TEXTURE_COMPARE_MODE: GL_NONE") LINE_TERMINATOR );
}
LogFile.Log( TEXT("\t") LINE_TERMINATOR );
}
void FOpenGLDebugFrameDumper::DumpTextureUnitSettings( FOutputDeviceFile& LogFile, GLint TextureUnitIndex )
{
glActiveTexture( GL_TEXTURE0+TextureUnitIndex );
ASSERT_NO_GL_ERROR();
bool bIsTextureBound = false;
GLint Binding;
glGetIntegerv( GL_TEXTURE_BINDING_1D, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_1D: %d") LINE_TERMINATOR, TextureUnitIndex, Binding );
DumpBoundTextureSettings( LogFile, GL_TEXTURE_1D );
bIsTextureBound = true;
}
glGetIntegerv( GL_TEXTURE_BINDING_2D, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_2D: %d") LINE_TERMINATOR, TextureUnitIndex, Binding );
DumpBoundTextureSettings( LogFile, GL_TEXTURE_2D );
bIsTextureBound = true;
}
glGetIntegerv( GL_TEXTURE_BINDING_3D, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_3D: %d") LINE_TERMINATOR, TextureUnitIndex, Binding );
DumpBoundTextureSettings( LogFile, GL_TEXTURE_3D );
bIsTextureBound = true;
}
glGetIntegerv( GL_TEXTURE_BINDING_CUBE_MAP, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_CUBE_MAP: %d") LINE_TERMINATOR, TextureUnitIndex, Binding );
DumpBoundTextureSettings( LogFile, GL_TEXTURE_CUBE_MAP );
bIsTextureBound = true;
}
glGetIntegerv( GL_TEXTURE_BINDING_2D_MULTISAMPLE, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_2D_MULTISAMPLE: %d") LINE_TERMINATOR, TextureUnitIndex, Binding );
DumpBoundTextureSurfaceSettings( LogFile, GL_TEXTURE_2D_MULTISAMPLE, 0, 0 );
bIsTextureBound = true;
}
glGetIntegerv( GL_TEXTURE_BINDING_BUFFER, &Binding );
ASSERT_NO_GL_ERROR();
if( Binding != 0 )
{
GLint DataStoreBinding;
glGetTexLevelParameteriv( GL_TEXTURE_BUFFER, 0, GL_TEXTURE_BUFFER_DATA_STORE_BINDING, &DataStoreBinding );
LogFile.Logf( TEXT("Unit %2d : GL_TEXTURE_BINDING_BUFFER: %d (bound buffer: %d)") LINE_TERMINATOR, TextureUnitIndex, Binding, DataStoreBinding );
}
if( bIsTextureBound )
{
GLint SamplerBinding;
glGetIntegerv( GL_SAMPLER_BINDING, &SamplerBinding );
ASSERT_NO_GL_ERROR();
if( SamplerBinding )
{
LogFile.Logf( TEXT("Unit %2d : GL_SAMPLER_BINDING: %d") LINE_TERMINATOR, TextureUnitIndex, SamplerBinding );
DumpBoundSamplerSettings( LogFile, SamplerBinding );
}
}
}
void FOpenGLDebugFrameDumper::DumpGeneralOpenGLState( const TCHAR* DrawCommandDescription, bool bIsDrawEvent, bool bIsFramebufferBlitEvent )
{
FString LogFileName = *CachedEventFolder / TEXT("state.log");
FOutputDeviceFile LogFile( *LogFileName );
LogFile.SetAutoEmitLineTerminator( false );
LogFile.Log( LINE_TERMINATOR ); // to end "log start" line
// Extract and log current event info
LogFile.Logf( TEXT( "Event: %s" ) LINE_TERMINATOR, DrawCommandDescription );
// Extract and log current OpenGL error
GLenum OpenGLError = glGetError();
const TCHAR* OpenGLErrorString = 0;
switch( OpenGLError )
{
case GL_NO_ERROR: OpenGLErrorString = TEXT("GL_NO_ERROR" ); break;
case GL_INVALID_ENUM: OpenGLErrorString = TEXT("GL_INVALID_ENUM" ); break;
case GL_INVALID_VALUE: OpenGLErrorString = TEXT("GL_INVALID_VALUE" ); break;
case GL_INVALID_OPERATION: OpenGLErrorString = TEXT("GL_INVALID_OPERATION" ); break;
case GL_OUT_OF_MEMORY: OpenGLErrorString = TEXT("GL_OUT_OF_MEMORY" ); break;
// case GL_STACK_OVERFLOW: OpenGLErrorString = TEXT("GL_STACK_OVERFLOW" ); break;
// case GL_STACK_UNDERFLOW: OpenGLErrorString = TEXT("GL_STACK_UNDERFLOW" ); break;
// case GL_TABLE_TOO_LARGE: OpenGLErrorString = TEXT("GL_TABLE_TOO_LARGE" ); break;
default: OpenGLErrorString = TEXT("unknown" ); break;
}
LogFile.Logf( TEXT( "OpenGL Error: %s ( 0x%x )" ) LINE_TERMINATOR, OpenGLErrorString, OpenGLError );
DumpRenderTargetsState( LogFile );
DumpDepthState( LogFile );
DumpStencilState( LogFile );
DumpBufferMasks( LogFile );
DumpClearValues( LogFile );
DumpMultisamplingSettings( LogFile );
DumpScissorAndViewport( LogFile );
if( bIsFramebufferBlitEvent || bIsDrawEvent )
{
DumpBufferBindings( LogFile );
}
if( bIsDrawEvent )
{
DumpVertexAttribArraysState( LogFile );
DumpBlendingState( LogFile );
DumpHintSettings( LogFile );
DumpOpenGLLimits( LogFile );
DumpPointsSettings( LogFile );
DumpLinesSettings( LogFile );
DumpPolygonsSettings( LogFile );
DumpTextureLimitsAndBindings( LogFile );
DumpProgramSettings( LogFile );
DumpLogicOpsSettings( LogFile );
DumpPixelModeSettings( LogFile );
}
LogFile.TearDown();
}
void FOpenGLDebugFrameDumper::DumpFramebufferState( bool bReadFramebuffer )
{
FString LogFileName;
GLenum FramebufferBindingEnum;
if( bReadFramebuffer )
{
LogFileName = TEXT("framebufferRead.log");
FramebufferBindingEnum = GL_READ_FRAMEBUFFER_BINDING;
}
else
{
LogFileName = TEXT("framebufferDraw.log");
FramebufferBindingEnum = GL_DRAW_FRAMEBUFFER_BINDING;
}
FString LogFilePath = *CachedEventFolder / LogFileName;
FOutputDeviceFile LogFile( *LogFilePath );
LogFile.SetAutoEmitLineTerminator( false );
LogFile.Log( LINE_TERMINATOR ); // to end "log start" line
GLint CurrentlyBoundFramebuffer;
glGetIntegerv( FramebufferBindingEnum, &CurrentlyBoundFramebuffer );
ASSERT_NO_GL_ERROR();
DumpFramebufferSettings( LogFile, CurrentlyBoundFramebuffer );
LogFile.TearDown();
}
void FOpenGLDebugFrameDumper::DumpProgramAndShaderState( void )
{
// Dump current program and its shaders state
GLint ProgramID;
glGetIntegerv( GL_CURRENT_PROGRAM, &ProgramID );
ASSERT_NO_GL_ERROR();
FString ProgramLogFileName = *CachedEventFolder / FString::Printf( TEXT("program%d.log"), ProgramID );
FOutputDeviceFile ProgramLogFile( *ProgramLogFileName );
ProgramLogFile.SetAutoEmitLineTerminator( false );
ProgramLogFile.Log( LINE_TERMINATOR ); // to end "log start" line
DumpProgramContents( ProgramLogFile, ProgramID );
ProgramLogFile.TearDown();
GLint AttachedShaderCount;
glGetProgramiv( ProgramID, GL_ATTACHED_SHADERS, &AttachedShaderCount );
ASSERT_NO_GL_ERROR();
if( !AttachedShaderCount )
{
// we're done here
return;
}
// Log attached shaders, into their own files
GLsizei CountReceived = 0;
GLuint* AttachedShadersTable = (GLuint*)FMemory::Malloc( sizeof(GLuint)*AttachedShaderCount );
glGetAttachedShaders( ProgramID, AttachedShaderCount, &CountReceived, AttachedShadersTable );
ASSERT_NO_GL_ERROR();
for( GLint AttachedShaderIndex = 0; AttachedShaderIndex < CountReceived; ++AttachedShaderIndex )
{
FString ShaderLogFileName = *CachedEventFolder / FString::Printf( TEXT("shader%d.log"), AttachedShadersTable[AttachedShaderIndex] );
FOutputDeviceFile ShaderLogFile( *ShaderLogFileName );
ShaderLogFile.SetAutoEmitLineTerminator( false );
ShaderLogFile.Log( LINE_TERMINATOR ); // to end "log start" line
DumpShaderContents( ShaderLogFile, AttachedShadersTable[AttachedShaderIndex] );
ShaderLogFile.TearDown();
}
FMemory::Free( AttachedShadersTable );
}
void FOpenGLDebugFrameDumper::DumpBoundTextureState( void )
{
FString LogFileName = *CachedEventFolder / TEXT("textureUnits.log");
FOutputDeviceFile LogFile( *LogFileName );
LogFile.SetAutoEmitLineTerminator( false );
LogFile.Log( LINE_TERMINATOR ); // to end "log start" line
// Remember this, as DumpTextureUnitSettings() modifies it
GLint ActiveTextureUnit;
glGetIntegerv( GL_ACTIVE_TEXTURE, &ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
GLint MaxTextureImageUnits;
glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &MaxTextureImageUnits );
ASSERT_NO_GL_ERROR();
for( GLint TextureUnitIndex = 0; TextureUnitIndex < MaxTextureImageUnits; ++TextureUnitIndex )
{
DumpTextureUnitSettings( LogFile, TextureUnitIndex );
}
// And restore previous OpenGL state
glActiveTexture( ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
LogFile.TearDown();
}
void FOpenGLDebugFrameDumper::DumpFramebufferContent( GLint FramebufferID, GLint AttachmentSlot, const TCHAR* TargetFilename, EFramebufferAttachmentSlotType::Type SlotType, bool bShouldFlipImageVertically )
{
// Remember those, as we'll need to restore them later
GLint CurrentlyBoundReadFramebuffer;
glGetIntegerv( GL_READ_FRAMEBUFFER_BINDING, &CurrentlyBoundReadFramebuffer );
ASSERT_NO_GL_ERROR();
GLint CurrentReadBuffer;
glGetIntegerv( GL_READ_BUFFER, &CurrentReadBuffer );
ASSERT_NO_GL_ERROR();
if( FramebufferID != CurrentlyBoundReadFramebuffer )
{
glBindFramebuffer( GL_READ_FRAMEBUFFER, FramebufferID );
ASSERT_NO_GL_ERROR();
}
if( ( SlotType == EFramebufferAttachmentSlotType::Color ) && ( AttachmentSlot != CurrentReadBuffer ) )
{
FOpenGL::ReadBuffer( AttachmentSlot ); // this only needs to be selected for reading from one of color buffers
ASSERT_NO_GL_ERROR();
}
GLint AttachmentType = GL_FRAMEBUFFER_DEFAULT;
if( FramebufferID )
{
glGetFramebufferAttachmentParameteriv( GL_READ_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
}
GLint Width = 0;
GLint Height = 0;
GLint Depth = 0;
GLint InternalFormat = 0;
bool bIsAllOk = true;
if( AttachmentType == GL_FRAMEBUFFER_DEFAULT )
{
uint32 BackbufferWidth;
uint32 BackbufferHeight;
PlatformGetBackbufferDimensions( BackbufferWidth, BackbufferHeight );
Width = BackbufferWidth;
Height = BackbufferHeight;
check( Width > 0 && Height > 0 );
// no matter what the real format is, this is only to select target format later.
if( SlotType == EFramebufferAttachmentSlotType::Depth )
{
InternalFormat = GL_DEPTH_COMPONENT32F;
}
else if( SlotType == EFramebufferAttachmentSlotType::Stencil )
{
InternalFormat = GL_DEPTH24_STENCIL8;
}
else
{
InternalFormat = GL_RGBA8;
}
}
else if( AttachmentType == GL_TEXTURE )
{
// We need to get texture information now
// Get texture ID for binding
GLint TextureID = 0;
glGetFramebufferAttachmentParameteriv( GL_READ_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &TextureID );
ASSERT_NO_GL_ERROR();
// Determine the level we need to ask about
GLint TextureLevel;
glGetFramebufferAttachmentParameteriv( GL_READ_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &TextureLevel );
ASSERT_NO_GL_ERROR();
// Determine if the texture we have attached is 2D or Cube ( I assume 3D and 1D make no sense )
GLint TextureCubeMapFace;
glGetFramebufferAttachmentParameteriv( GL_READ_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, &TextureCubeMapFace );
ASSERT_NO_GL_ERROR();
bool bIsCube = ( TextureCubeMapFace != 0 );
// Setup various types of function call parameters, depending on whether it is cube or not
GLenum TextureTypeAsk;
GLenum TextureTypeSet;
GLenum TextureTypeFace;
if( bIsCube )
{
TextureTypeAsk = GL_TEXTURE_BINDING_CUBE_MAP;
TextureTypeSet = GL_TEXTURE_CUBE_MAP;
TextureTypeFace = TextureCubeMapFace;
}
else
{
TextureTypeAsk = GL_TEXTURE_BINDING_2D;
TextureTypeSet = GL_TEXTURE_2D;
TextureTypeFace = GL_TEXTURE_2D;
}
// Remember what's bound to the stage we'll bind our texture to
GLint BoundTextureID;
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
ASSERT_NO_GL_ERROR();
// Bind our texture. Was it GL_TEXTURE_2D?
ASSERT_NO_GL_ERROR();
GDisableOpenGLDebugOutput = true;
glBindTexture( TextureTypeSet, TextureID );
glFinish();
GDisableOpenGLDebugOutput = false;
if( glGetError() )
{
bIsAllOk = false;
// It could be GL_TEXTURE_2D_MULTISAMPLE then?
check(TextureTypeSet == GL_TEXTURE_2D);
check(TextureLevel == 0);
TextureTypeAsk = GL_TEXTURE_BINDING_2D_MULTISAMPLE;
TextureTypeSet = GL_TEXTURE_2D_MULTISAMPLE;
TextureTypeFace = GL_TEXTURE_2D_MULTISAMPLE;
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
ASSERT_NO_GL_ERROR();
GDisableOpenGLDebugOutput = true;
glBindTexture( TextureTypeSet, TextureID );
glFinish();
GDisableOpenGLDebugOutput = false;
if( glGetError() )
{
// Ok, then GL_TEXTURE_3D?
TextureTypeAsk = GL_TEXTURE_BINDING_3D;
TextureTypeSet = GL_TEXTURE_3D;
TextureTypeFace = GL_TEXTURE_3D;
glGetIntegerv( TextureTypeAsk, &BoundTextureID );
ASSERT_NO_GL_ERROR();
glBindTexture( TextureTypeSet, TextureID );
ASSERT_NO_GL_ERROR();
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Texture %d is 3D - dumping data from such is unhandled atm. Add code?"), TextureID );
}
else
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Texture %d is multisampled - dumping data from such is unhandled atm. Add code?"), TextureID );
}
}
// Get the information we need
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_WIDTH, &Width );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_HEIGHT, &Height );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_DEPTH, &Depth );
ASSERT_NO_GL_ERROR();
glGetTexLevelParameteriv( TextureTypeFace, TextureLevel, GL_TEXTURE_INTERNAL_FORMAT, &InternalFormat );
ASSERT_NO_GL_ERROR();
// Restore previous binding to this stage
if( BoundTextureID != TextureID )
{
glBindTexture( TextureTypeSet, BoundTextureID );
ASSERT_NO_GL_ERROR();
}
}
else if( AttachmentType == GL_RENDERBUFFER )
{
// We need to get renderbuffer information now
// Get renderbuffer ID for binding
GLint RenderbufferID = 0;
glGetFramebufferAttachmentParameteriv( GL_READ_FRAMEBUFFER, AttachmentSlot, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &RenderbufferID );
ASSERT_NO_GL_ERROR();
// Remember what's bound as renderbuffer now, to restore it later
GLint CurrentlyBoundRenderbuffer;
glGetIntegerv( GL_RENDERBUFFER_BINDING, &CurrentlyBoundRenderbuffer );
ASSERT_NO_GL_ERROR();
// Bind our renderbuffer
if( RenderbufferID != CurrentlyBoundRenderbuffer )
{
glBindRenderbuffer( GL_RENDERBUFFER, RenderbufferID );
ASSERT_NO_GL_ERROR();
}
GLint Samples;
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &Samples );
ASSERT_NO_GL_ERROR();
if( Samples != 0 )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Renderbuffer %d is multisampled - dumping data from such is unhandled atm. Add code?"), RenderbufferID );
bIsAllOk = false;
}
else
{
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &Width );
ASSERT_NO_GL_ERROR();
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &Height );
ASSERT_NO_GL_ERROR();
glGetRenderbufferParameteriv( GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &InternalFormat );
ASSERT_NO_GL_ERROR();
}
// Restore previous state
if( RenderbufferID != CurrentlyBoundRenderbuffer )
{
glBindRenderbuffer( GL_RENDERBUFFER, CurrentlyBoundRenderbuffer );
ASSERT_NO_GL_ERROR();
}
}
else if( AttachmentType == GL_NONE )
{
bIsAllOk = false;
}
else
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unrecognized framebuffer attachment type: %d! Debug this to add handling for it?."), AttachmentType );
bIsAllOk = false;
}
if( bIsAllOk )
{
FString FilenameString( TargetFilename );
int32 RGBADataSize = 4 * Width * Height;
uint8* RGBAData = (uint8*)FMemory::Malloc( RGBADataSize );
bool bIgnoreAlpha = false;
switch( InternalFormat )
{
// These formats can reasonably be expected never to contain values from outside 0-1 range,
// and can be taken into buffer directly. All missing R,G,B components will be filled with 0s, missing alpha with 1s.
case GL_RG8:
case GL_RG16:
case GL_R8:
case GL_R16:
case GL_RGB8:
case GL_RGB5:
case GL_R3_G3_B2:
case GL_RGB4:
bIgnoreAlpha = true;
// pass-through
case GL_RGBA8:
case GL_RGBA12:
case GL_RGBA16:
case GL_RGB10_A2:
case GL_RGBA4:
case GL_RGB5_A1:
case GL_SRGB8_ALPHA8:
if( SlotType != EFramebufferAttachmentSlotType::Color )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Trying to receive depth or stencil information from color attachment! Internal format: %s"), GetGLInternalFormatString( InternalFormat ) );
bIsAllOk = false;
}
else
{
glReadPixels( 0, 0, Width, Height, TextureOutputFormat, GL_UNSIGNED_INT_8_8_8_8_REV, RGBAData );
ASSERT_NO_GL_ERROR();
}
break;
case GL_RG16F:
case GL_RG32F:
case GL_R16F:
case GL_R32F:
case GL_R11F_G11F_B10F:
bIgnoreAlpha = true;
// pass-through
case GL_RGBA32F:
case GL_RGBA16F:
if( SlotType != EFramebufferAttachmentSlotType::Color )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Trying to receive depth or stencil information from color attachment! Internal format: %s"), GetGLInternalFormatString( InternalFormat ) );
bIsAllOk = false;
}
else
{
// These formats are stored internally as floats, and might conceivably contain values from outside 0-1 range.
// Let's take the data from them into float buffer, check up on it, and if we detect values from outside 0-1 range,
// report it and scale all components down by the same amount, so they fit in 0-1 range.
int32 FloatRGBADataSize = sizeof(float) * RGBADataSize;
float* FloatRGBAData = (float*)FMemory::Malloc( FloatRGBADataSize );
glReadPixels( 0, 0, Width, Height, TextureOutputFormat, GL_FLOAT, FloatRGBAData );
ASSERT_NO_GL_ERROR();
int32 PixelComponentCount = RGBADataSize;
// Determine minimal and maximal float value present in received data. Treat alpha separately.
float MinValue[2] = { FLT_MAX };
float MaxValue[2] = { FLT_MIN };
float* DataPtr = FloatRGBAData;
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
int32 AlphaValue = ( PixelComponentIndex % 4 == 3 ) ? 1 : 0;
if( *DataPtr < MinValue[AlphaValue] )
{
MinValue[AlphaValue] = *DataPtr;
}
if( *DataPtr > MaxValue[AlphaValue] )
{
MaxValue[AlphaValue] = *DataPtr;
}
}
// If necessary, rescale the data, announcing this fact.
if( ( MinValue[0] < 0.f ) || ( MaxValue[0] > 1.f ) )
{
DataPtr = FloatRGBAData;
float RescaleFactor = MaxValue[0] - MinValue[0];
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
if( PixelComponentIndex % 4 != 3 )
{
*DataPtr = ( *DataPtr - MinValue[0] ) / RescaleFactor;
}
}
// Add '_min%f_max%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_min%f_max%f"), MinValue[0], MaxValue[0] );
}
if( !bIgnoreAlpha && ( ( MinValue[1] < 0.f ) || ( MaxValue[1] > 1.f ) ) )
{
DataPtr = FloatRGBAData;
float RescaleFactor = MaxValue[1] - MinValue[1];
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
if( PixelComponentIndex % 4 == 3 )
{
*DataPtr = ( *DataPtr - MinValue[1] ) / RescaleFactor;
}
}
// Add '_amin%f_amax%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_amin%f_amax%f"), MinValue[1], MaxValue[1] );
}
// Convert the data into RGBA8 buffer
DataPtr = FloatRGBAData;
uint8* TargetPtr = RGBAData;
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex )
{
*TargetPtr++ = (uint8)( *DataPtr++ * 255.0f );
}
FMemory::Free( FloatRGBAData );
}
break;
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT24:
case GL_DEPTH_COMPONENT32F:
if( SlotType != EFramebufferAttachmentSlotType::Depth )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Trying to receive color or stencil information from depth attachment! Internal format: %s"), GetGLInternalFormatString( InternalFormat ) );
bIsAllOk = false;
}
else
{
// However these formats are stored internally, they can be received as floats.
int32 DepthValueCount = RGBADataSize/4;
int32 FloatDepthDataSize = sizeof(float) * DepthValueCount;
float* FloatDepthData = (float*)FMemory::Malloc( FloatDepthDataSize );
glReadPixels( 0, 0, Width, Height, GL_DEPTH_COMPONENT, GL_FLOAT, FloatDepthData );
ASSERT_NO_GL_ERROR();
// Determine minimal and maximal float value present in received data
float MinValue = FLT_MAX;
float MaxValue = FLT_MIN;
float* DataPtr = FloatDepthData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
if( *DataPtr < MinValue )
{
MinValue = *DataPtr;
}
if( *DataPtr > MaxValue )
{
MaxValue = *DataPtr;
}
}
// If necessary, rescale the data.
if( ( MinValue < 0.f ) || ( MaxValue > 1.f ) )
{
DataPtr = FloatDepthData;
float RescaleFactor = MaxValue - MinValue;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
*DataPtr = ( *DataPtr - MinValue ) / RescaleFactor;
}
// Add '_min%f_max%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_min%f_max%f"), MinValue, MaxValue );
}
// Convert the data into rgba8 buffer
DataPtr = FloatDepthData;
uint8* TargetPtr = RGBAData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex )
{
uint8 Value = (uint8)( *DataPtr++ * 255.0f );
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = 255;
}
FMemory::Free( FloatDepthData );
}
break;
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
if( SlotType == EFramebufferAttachmentSlotType::Color )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Trying to receive color information from depth stencil attachment! Internal format: %s"), GetGLInternalFormatString( InternalFormat ) );
bIsAllOk = false;
}
else if( SlotType == EFramebufferAttachmentSlotType::Depth )
{
// However these formats are stored internally, they can be received as floats.
int32 DepthValueCount = RGBADataSize/4;
int32 FloatDepthDataSize = sizeof(float) * DepthValueCount;
float* FloatDepthData = (float*)FMemory::Malloc( FloatDepthDataSize );
glReadPixels( 0, 0, Width, Height, GL_DEPTH_COMPONENT, GL_FLOAT, FloatDepthData );
ASSERT_NO_GL_ERROR();
// Determine minimal and maximal float value present in received data
float MinValue = FLT_MAX;
float MaxValue = FLT_MIN;
float* DataPtr = FloatDepthData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
if( *DataPtr < MinValue )
{
MinValue = *DataPtr;
}
if( *DataPtr > MaxValue )
{
MaxValue = *DataPtr;
}
}
// If necessary, rescale the data.
if( ( MinValue < 0.f ) || ( MaxValue > 1.f ) )
{
DataPtr = FloatDepthData;
float RescaleFactor = MaxValue - MinValue;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
*DataPtr = ( *DataPtr - MinValue ) / RescaleFactor;
}
// Add '_min%f_max%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_min%f_max%f"), MinValue, MaxValue );
}
// Convert the data into rgba8 buffer
DataPtr = FloatDepthData;
uint8* TargetPtr = RGBAData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex )
{
uint8 Value = (uint8)( *DataPtr++ * 255.0f );
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = 255;
}
FMemory::Free( FloatDepthData );
}
else if( SlotType == EFramebufferAttachmentSlotType::Stencil )
{
int32 StencilValueCount = RGBADataSize/4;
uint8* StencilData = (uint8*)FMemory::Malloc( StencilValueCount );
glReadPixels( 0, 0, Width, Height, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, StencilData ); // or GL_STENCIL_INDEX8 ?
ASSERT_NO_GL_ERROR();
// Convert the data into rgba8 buffer
uint8* DataPtr = StencilData;
uint8* TargetPtr = RGBAData;
for( int32 StencilValueIndex = 0; StencilValueIndex < StencilValueCount; ++StencilValueIndex )
{
uint8 Value = *DataPtr++;
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = 255;
}
FMemory::Free( StencilData );
}
break;
default:
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unhandled internal texture format: %s (0x%x)!"), GetGLInternalFormatString( InternalFormat ), InternalFormat );
bIsAllOk = false;
break;
}
if( bIsAllOk )
{
if( bShouldFlipImageVertically )
{
// Flip image vertically, in-place
uint32 Pitch = 4 * Width;
uint8* LineBuffer = (uint8*)FMemory::Malloc( Pitch );
for( int32 ImageRowIndex = 0; ImageRowIndex < Height / 2; ++ImageRowIndex )
{
memcpy( LineBuffer, RGBAData + ImageRowIndex * Pitch, Pitch );
memcpy( RGBAData + ImageRowIndex * Pitch, RGBAData + ( Height - 1 - ImageRowIndex ) * Pitch, Pitch );
memcpy( RGBAData + ( Height - 1 - ImageRowIndex ) * Pitch, LineBuffer, Pitch );
}
FMemory::Free( LineBuffer );
}
#if USE_COMPRESSED_PNG_INSTEAD_OF_BMP_FOR_CONTENT_OUTPUT
FilenameString += TEXT(".png");
FString FilePath = *CachedEventFolder / FilenameString;
if( bIgnoreAlpha && ( SlotType == EFramebufferAttachmentSlotType::Color ) ) // Alpha is 255 in depth and stencil already
{
// Make image non-transparent
uint8* DataPtr = RGBAData+3; // first alpha offset
for( int32 PixelComponentIndex = 0; PixelComponentIndex < RGBADataSize/4; ++PixelComponentIndex )
{
*DataPtr = 255;
DataPtr += 4;
}
}
appCreatePNGWithAlpha( *FilePath, Width, Height, (FColor*)RGBAData );
#else
FilenameString += TEXT(".bmp");
FString FilePath = *CachedEventFolder / FilenameString;
if( bIgnoreAlpha )
{
FFileHelper::CreateBitmap( *FilePath, Width, Height, (FColor*)RGBAData );
}
else
{
appCreateBitmapWithAlpha( *FilePath, Width, Height, (FColor*)RGBAData );
}
#endif
}
FMemory::Free( RGBAData );
}
// Restore previous state
if( FramebufferID != CurrentlyBoundReadFramebuffer )
{
glBindFramebuffer( GL_READ_FRAMEBUFFER, CurrentlyBoundReadFramebuffer );
ASSERT_NO_GL_ERROR();
}
FOpenGL::ReadBuffer( CurrentReadBuffer );
ASSERT_NO_GL_ERROR();
}
void FOpenGLDebugFrameDumper::DumpFramebufferContents( bool bReadFramebuffer )
{
FString LogFileEnding;
GLenum FramebufferBindingEnum;
GLenum FramebufferTypeEnum;
if( bReadFramebuffer )
{
LogFileEnding = TEXT("Read");
FramebufferBindingEnum = GL_READ_FRAMEBUFFER_BINDING;
FramebufferTypeEnum = GL_READ_FRAMEBUFFER;
}
else
{
LogFileEnding = TEXT("Draw");
FramebufferBindingEnum = GL_DRAW_FRAMEBUFFER_BINDING;
FramebufferTypeEnum = GL_DRAW_FRAMEBUFFER;
}
GLint CurrentlyBoundFramebuffer;
glGetIntegerv( FramebufferBindingEnum, &CurrentlyBoundFramebuffer );
ASSERT_NO_GL_ERROR();
if( CurrentlyBoundFramebuffer == 0 )
{
// Screen buffer. Assumming it always has a front and back buffer.
DumpFramebufferContent( 0, GL_FRONT_LEFT, *FString::Printf( TEXT("fbScreenFront%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Color, true );
DumpFramebufferContent( 0, GL_BACK_LEFT, *FString::Printf( TEXT("fbScreenBack%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Color, true );
#if PLATFORM_MAC
GLint AttachmentType;
glGetFramebufferAttachmentParameteriv( FramebufferTypeEnum, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType != GL_NONE )
{
DumpFramebufferContent( 0, GL_DEPTH, *FString::Printf( TEXT("fbScreenDepth%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Depth, true );
}
glGetFramebufferAttachmentParameteriv( FramebufferTypeEnum, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType != GL_NONE )
{
DumpFramebufferContent( 0, GL_STENCIL, *FString::Printf( TEXT("fbScreenStencil%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Stencil, true );
}
#else
DumpFramebufferContent( 0, GL_DEPTH, *FString::Printf( TEXT("fbScreenDepth%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Depth, true );
// Commented out as Windows OpenGL provides no way to find out if screen buffer contains stencil, and causes OpenGL errors when it doesn't contain it.
// DumpFramebufferContent( 0, GL_STENCIL, *FString::Printf( TEXT("fbScreenStencil%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Stencil, true );
#endif
}
else
{
GLint MaxColorAttachments;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &MaxColorAttachments );
ASSERT_NO_GL_ERROR();
GLint AttachmentType;
for( int32 AttachmentIndex = 0; AttachmentIndex < MaxColorAttachments; ++AttachmentIndex )
{
glGetFramebufferAttachmentParameteriv( FramebufferTypeEnum, GL_COLOR_ATTACHMENT0 + AttachmentIndex, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType != GL_NONE )
{
DumpFramebufferContent( CurrentlyBoundFramebuffer, GL_COLOR_ATTACHMENT0 + AttachmentIndex, *FString::Printf( TEXT("fb%d%s"), AttachmentIndex, *LogFileEnding ), EFramebufferAttachmentSlotType::Color, false );
}
}
glGetFramebufferAttachmentParameteriv( FramebufferTypeEnum, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType != GL_NONE )
{
DumpFramebufferContent( CurrentlyBoundFramebuffer, GL_DEPTH_ATTACHMENT, *FString::Printf( TEXT("fbDepth%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Depth, false );
}
glGetFramebufferAttachmentParameteriv( FramebufferTypeEnum, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &AttachmentType );
ASSERT_NO_GL_ERROR();
if( AttachmentType != GL_NONE )
{
DumpFramebufferContent( CurrentlyBoundFramebuffer, GL_STENCIL_ATTACHMENT, *FString::Printf( TEXT("fbStencil%s"), *LogFileEnding ), EFramebufferAttachmentSlotType::Stencil, false );
}
}
}
void FOpenGLDebugFrameDumper::DumpTextureSurfaceContent( const TCHAR* TargetFilename, GLenum SurfaceType, GLint Level )
{
GLint Samples;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_SAMPLES, &Samples );
ASSERT_NO_GL_ERROR();
if( Samples != 0 )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Texture surface we try to get data for is multisampled! Add code to handle this when you need it.") );
return;
}
GLint InternalFormat;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_INTERNAL_FORMAT, &InternalFormat );
ASSERT_NO_GL_ERROR();
GLint Width;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_WIDTH, &Width );
ASSERT_NO_GL_ERROR();
GLint Height;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_HEIGHT, &Height );
ASSERT_NO_GL_ERROR();
GLint Compressed;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_COMPRESSED, &Compressed );
ASSERT_NO_GL_ERROR();
if( Compressed != 0 )
{
// Save specific level and target (face) of a texture in a DDS file
GLint CompressedSize;
glGetTexLevelParameteriv( SurfaceType, Level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &CompressedSize );
ASSERT_NO_GL_ERROR();
uint8* CompressedData = (uint8*)FMemory::Malloc( CompressedSize );
glGetCompressedTexImage( SurfaceType, Level, CompressedData );
ASSERT_NO_GL_ERROR();
FString FilenameString( TargetFilename );
FString FilePath = *CachedEventFolder / ( FilenameString + TEXT(".dds") );
appCreateDDSWithSingleSurface( *FilePath, Width, Height, InternalFormat, CompressedData, CompressedSize );
FMemory::Free( CompressedData );
}
else
{
FString FilenameString( TargetFilename );
// Save non-compressed format
int32 RGBADataSize = 4 * Width * Height;
uint8* RGBAData = (uint8*)FMemory::Malloc( RGBADataSize );
bool bIgnoreAlpha = false;
bool bTextureTypeIsColor = false;
switch( InternalFormat )
{
// These texture formats can reasonably be expected never to contain values from outside 0-1 range,
// and can be taken into buffer directly. All missing R,G,B components will be filled with 0s, missing alpha with 1s.
case GL_RG8:
case GL_RG16:
case GL_R8:
case GL_R16:
case GL_RGB8:
case GL_RGB5:
case GL_R3_G3_B2:
case GL_RGB4:
bIgnoreAlpha = true;
// pass-through
case GL_RGBA8:
case GL_RGBA12:
case GL_RGBA16:
case GL_RGB10_A2:
case GL_RGBA4:
case GL_RGB5_A1:
case GL_SRGB8_ALPHA8:
glGetTexImage( SurfaceType, Level, TextureOutputFormat, GL_UNSIGNED_INT_8_8_8_8_REV, RGBAData );
ASSERT_NO_GL_ERROR();
bTextureTypeIsColor = true;
break;
case GL_RG16F:
case GL_RG32F:
case GL_R16F:
case GL_R32F:
case GL_R11F_G11F_B10F:
bIgnoreAlpha = true;
// pass-through
case GL_RGBA32F:
case GL_RGBA16F:
// These texture formats are stored internally as floats, and might conceivably contain values from outside 0-1 range.
// Let's take the data from them into float buffer, check up on it, and if we detect values from outside 0-1 range,
// report it and scale all components down by the same amount, so they fit in 0-1 range.
bTextureTypeIsColor = true;
{
// Determine minimal and maximal float value present in received data. Treat alpha separately.
int32 FloatRGBADataSize = sizeof(float) * RGBADataSize;
float* FloatRGBAData = (float*)FMemory::Malloc( FloatRGBADataSize );
glGetTexImage( SurfaceType, Level, TextureOutputFormat, GL_FLOAT, FloatRGBAData );
ASSERT_NO_GL_ERROR();
int32 PixelComponentCount = RGBADataSize;
// Determine minimal and maximal float value present in received data. Treat alpha separately.
float MinValue[2] = { FLT_MAX };
float MaxValue[2] = { FLT_MIN };
float* DataPtr = FloatRGBAData;
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
int32 AlphaValue = ( PixelComponentIndex % 4 == 3 ) ? 1 : 0;
if( *DataPtr < MinValue[AlphaValue] )
{
MinValue[AlphaValue] = *DataPtr;
}
if( *DataPtr > MaxValue[AlphaValue] )
{
MaxValue[AlphaValue] = *DataPtr;
}
}
// If necessary, rescale the data.
if( ( MinValue[0] < 0.f ) || ( MaxValue[0] > 1.f ) )
{
DataPtr = FloatRGBAData;
float RescaleFactor = MaxValue[0] - MinValue[0];
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
if( PixelComponentIndex % 4 != 3 )
{
*DataPtr = ( *DataPtr - MinValue[0] ) / RescaleFactor;
}
}
// Add '_min%f_max%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_min%f_max%f"), MinValue[0], MaxValue[0] );
}
if( !bIgnoreAlpha && ( ( MinValue[1] < 0.f ) || ( MaxValue[1] > 1.f ) ) )
{
DataPtr = FloatRGBAData;
float RescaleFactor = MaxValue[1] - MinValue[1];
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex, ++DataPtr )
{
if( PixelComponentIndex % 4 == 3 )
{
*DataPtr = ( *DataPtr - MinValue[1] ) / RescaleFactor;
}
}
// Add '_amin%f_amax%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_amin%f_amax%f"), MinValue[1], MaxValue[1] );
}
// Convert the data into RGBA8 buffer
DataPtr = FloatRGBAData;
uint8* TargetPtr = RGBAData;
for( int32 PixelComponentIndex = 0; PixelComponentIndex < PixelComponentCount; ++PixelComponentIndex )
{
*TargetPtr++ = (uint8)( *DataPtr++ * 255.0f );
}
FMemory::Free( FloatRGBAData );
}
break;
case GL_DEPTH_COMPONENT32F:
case GL_DEPTH32F_STENCIL8:
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT24:
case GL_DEPTH24_STENCIL8:
// However these formats are stored internally, they can be received as floats.
// Not trying to get a stencil component out of a texture - assumming it's always depth that's interesting.
{
int32 DepthValueCount = RGBADataSize/4;
int32 FloatDepthDataSize = sizeof(float) * DepthValueCount;
float* FloatDepthData = (float*)FMemory::Malloc( FloatDepthDataSize );
glGetTexImage( SurfaceType, Level, GL_DEPTH_COMPONENT, GL_FLOAT, FloatDepthData );
ASSERT_NO_GL_ERROR();
// Determine minimal and maximal float value present in received data
float MinValue = FLT_MAX;
float MaxValue = FLT_MIN;
float* DataPtr = FloatDepthData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
if( *DataPtr < MinValue )
{
MinValue = *DataPtr;
}
if( *DataPtr > MaxValue )
{
MaxValue = *DataPtr;
}
}
// If necessary, rescale the data.
if( ( MinValue < 0.f ) || ( MaxValue > 1.f ) )
{
DataPtr = FloatDepthData;
float RescaleFactor = MaxValue - MinValue;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex, ++DataPtr )
{
*DataPtr = ( *DataPtr - MinValue ) / RescaleFactor;
}
// Add '_min%f_max%f' to end of file name, to let people know it's been rescaled.
FilenameString += FString::Printf( TEXT("_min%f_max%f"), MinValue, MaxValue );
}
// Convert the data into rgba8 buffer
DataPtr = FloatDepthData;
uint8* TargetPtr = RGBAData;
for( int32 DepthValueIndex = 0; DepthValueIndex < DepthValueCount; ++DepthValueIndex )
{
uint8 Value = (uint8)( *DataPtr++ * 255.0f );
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = Value;
*TargetPtr++ = 255;
}
FMemory::Free( FloatDepthData );
bIgnoreAlpha = true;
}
break;
default:
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unhandled internal texture format: %s!"), GetGLInternalFormatString( InternalFormat ) );
return;
}
{
// Flip image vertically (texture image data is stored in different row order than on Windows)
uint32 Pitch = 4 * Width;
uint8* LineBuffer = (uint8*)FMemory::Malloc( Pitch );
for( int32 ImageRowIndex = 0; ImageRowIndex < Height / 2; ++ImageRowIndex )
{
memcpy( LineBuffer, RGBAData + ImageRowIndex * Pitch, Pitch );
memcpy( RGBAData + ImageRowIndex * Pitch, RGBAData + ( Height - 1 - ImageRowIndex ) * Pitch, Pitch );
memcpy( RGBAData + ( Height - 1 - ImageRowIndex ) * Pitch, LineBuffer, Pitch );
}
FMemory::Free( LineBuffer );
}
#if USE_COMPRESSED_PNG_INSTEAD_OF_BMP_FOR_CONTENT_OUTPUT
FilenameString += TEXT(".png");
FString FilePath = *CachedEventFolder / FilenameString;
if( bIgnoreAlpha && bTextureTypeIsColor )
{
// Make image non-transparent
uint8* DataPtr = RGBAData+3; // first alpha offset
for( int32 PixelComponentIndex = 0; PixelComponentIndex < RGBADataSize/4; ++PixelComponentIndex )
{
*DataPtr = 255;
DataPtr += 4;
}
}
appCreatePNGWithAlpha( *FilePath, Width, Height, (FColor*)RGBAData );
#else
FilenameString += TEXT(".bmp");
FString FilePath = *CachedEventFolder / FilenameString;
if( bIgnoreAlpha )
{
FFileHelper::CreateBitmap( *FilePath, Width, Height, (FColor*)RGBAData );
}
else
{
appCreateBitmapWithAlpha( *FilePath, Width, Height, (FColor*)RGBAData );
}
#endif
FMemory::Free( RGBAData );
}
}
void FOpenGLDebugFrameDumper::DumpTextureContentForImageUnit( int32 UnitIndex )
{
glActiveTexture( GL_TEXTURE0+UnitIndex );
ASSERT_NO_GL_ERROR();
GLint TextureID;
glGetIntegerv( GL_TEXTURE_BINDING_1D, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unit %d, texture binding 1D = %d, texture dump unhandled!"), UnitIndex, TextureID );
}
glGetIntegerv( GL_TEXTURE_BINDING_2D, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
GLint BaseLevel;
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, &BaseLevel );
ASSERT_NO_GL_ERROR();
GLint MaxLevel;
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &MaxLevel );
ASSERT_NO_GL_ERROR();
for( GLint Level = BaseLevel; Level <= MaxLevel; ++Level )
{
DumpTextureSurfaceContent( *FString::Printf( TEXT("tex%d_2D_id%d_lvl%d"), UnitIndex, TextureID, Level ), GL_TEXTURE_2D, Level );
}
}
glGetIntegerv( GL_TEXTURE_BINDING_2D_MULTISAMPLE, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unit %d, texture binding 2D multisample = %d, texture dump unhandled!"), UnitIndex, TextureID );
}
glGetIntegerv( GL_TEXTURE_BINDING_3D, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unit %d, texture binding 3D = %d, texture dump unhandled!"), UnitIndex, TextureID );
}
glGetIntegerv( GL_TEXTURE_BINDING_BUFFER, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Unit %d, texture binding buffer = %d, texture dump unhandled!"), UnitIndex, TextureID );
}
glGetIntegerv( GL_TEXTURE_BINDING_CUBE_MAP, &TextureID );
ASSERT_NO_GL_ERROR();
if( TextureID )
{
GLint BaseLevel;
glGetTexParameteriv( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, &BaseLevel );
ASSERT_NO_GL_ERROR();
GLint MaxLevel;
glGetTexParameteriv( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, &MaxLevel );
ASSERT_NO_GL_ERROR();
for( GLint Level = BaseLevel; Level <= MaxLevel; ++Level )
{
for( GLint Face = 0; Face < 6; ++Face )
{
DumpTextureSurfaceContent( *FString::Printf( TEXT("tex%d_2D_id%d_lvl%d_face%d"), UnitIndex, TextureID, Level, Face ), GL_TEXTURE_CUBE_MAP_POSITIVE_X+Face, Level );
}
}
}
}
void FOpenGLDebugFrameDumper::DumpBoundTexturesContents( void )
{
// Remember this value, as we need to restore it
GLint ActiveTextureUnit;
glGetIntegerv( GL_ACTIVE_TEXTURE, &ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
GLint MaxTextureImageUnits;
glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &MaxTextureImageUnits );
ASSERT_NO_GL_ERROR();
for( int32 TextureImageUnitIndex = 0; TextureImageUnitIndex < MaxTextureImageUnits; ++TextureImageUnitIndex )
{
DumpTextureContentForImageUnit( TextureImageUnitIndex );
}
// Now restore value
glActiveTexture( ActiveTextureUnit );
ASSERT_NO_GL_ERROR();
}
void FOpenGLDebugFrameDumper::DumpElementArrayBufferContents( GLenum ElementArrayType )
{
GLint ElementArrayBufferBinding;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &ElementArrayBufferBinding );
ASSERT_NO_GL_ERROR();
if( ElementArrayBufferBinding == 0 )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: No valid OpenGL buffer bound to element array buffer binding point!") );
return;
}
GLint BufferMapped;
glGetBufferParameteriv( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_MAPPED, &BufferMapped );
ASSERT_NO_GL_ERROR();
if( BufferMapped )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Can't map element array buffer %d for reading contents, as it's currently mapped!"), ElementArrayBufferBinding );
return;
}
bool bIs32Bit = ( ElementArrayType == GL_UNSIGNED_INT );
GLint64 TotalBufferSize = 0LL; // getters apparently like to overwrite just some bytes of this on Mac, if they return small number, so we need to put 0 in both dwords initially
glGetBufferParameteri64v( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &TotalBufferSize );
ASSERT_NO_GL_ERROR();
GLuint BufferSize = (GLuint)TotalBufferSize; // yes, I know it's conversion to shorter variable, I don't expect buffers > 4GB in size.
void* BufferPtr = glMapBufferRange( GL_ELEMENT_ARRAY_BUFFER, 0, BufferSize, GL_MAP_READ_BIT );
ASSERT_NO_GL_ERROR();
if( !BufferPtr )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Failed to map element array buffer %d!"), ElementArrayBufferBinding );
return;
}
FString LogFileName = *CachedEventFolder / TEXT("elementArrayBuffer.log");
FOutputDeviceFile LogFile( *LogFileName );
LogFile.SetAutoEmitLineTerminator( false );
LogFile.Log( LINE_TERMINATOR ); // to end "log start" line
uint32 ElementCount = BufferSize / ( bIs32Bit ? sizeof(uint32) : sizeof(uint16) );
LogFile.Logf( TEXT("Index buffer %d, size %u, element count %d, %s") LINE_TERMINATOR, ElementArrayBufferBinding, BufferSize, ElementCount, bIs32Bit ? TEXT("32-bit") : TEXT("16-bit") );
LogFile.Log( TEXT("=========================================================================") LINE_TERMINATOR );
FString Line = TEXT("");
int32 ValuesInLine = 0;
uint32* Ptr32 = (uint32*)BufferPtr;
uint16* Ptr16 = (uint16*)BufferPtr;
uint32 LowestValue = 0xFFFFFFFF;
uint32 HighestValue = 0L;
for( uint32 ElementIndex = 0; ElementIndex < ElementCount; ++ElementIndex )
{
Line += ( ValuesInLine ? TEXT(", ") : TEXT( "\t" ) );
uint32 Value = ( bIs32Bit ? *Ptr32++ : *Ptr16++ );
if( LowestValue > Value )
{
LowestValue = Value;
}
if( HighestValue < Value )
{
HighestValue = Value;
}
Line += FString::Printf( TEXT("%u"), Value );
++ValuesInLine;
if( ValuesInLine >= 20 )
{
// Log line
Line += TEXT( ",\n" );
LogFile.Log( *Line );
Line = TEXT("");
ValuesInLine = 0;
}
}
if( ValuesInLine )
{
// Move last line to file
Line += LINE_TERMINATOR;
LogFile.Log( *Line );
}
LogFile.Log( TEXT("=========================================================================") LINE_TERMINATOR );
LogFile.Logf( TEXT("Lowest value in buffer: %u, highest: %u") LINE_TERMINATOR, LowestValue, HighestValue );
LogFile.TearDown();
glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
ASSERT_NO_GL_ERROR();
}
inline uint32 HalfFloatToFloatInteger( uint16 HalfFloat )
{
register uint32 Sign = ( HalfFloat >> 15 ) & 0x00000001;
register uint32 Exponent = ( HalfFloat >> 10 ) & 0x0000001f;
register uint32 Mantiss = HalfFloat & 0x000003ff;
if( Exponent == 0 )
{
if( Mantiss == 0 ) // Plus or minus zero
{
return Sign << 31;
}
else // Denormalized number -- renormalize it
{
while( ( Mantiss & 0x00000400 ) == 0 )
{
Mantiss <<= 1;
Exponent -= 1;
}
Exponent += 1;
Mantiss &= ~0x00000400;
}
}
else if( Exponent == 31 )
{
if( Mantiss == 0 ) // Inf
return ( Sign << 31 ) | 0x7f800000;
else // NaN
return ( Sign << 31 ) | 0x7f800000 | ( Mantiss << 13 );
}
Exponent = Exponent + ( 127 - 15 );
Mantiss = Mantiss << 13;
return ( Sign << 31 ) | ( Exponent << 23 ) | Mantiss;
}
inline float HalfFloatToFloat( uint16 HalfFloat )
{
union
{
float F;
uint32 I;
} Convert;
Convert.I = HalfFloatToFloatInteger( HalfFloat );
return Convert.F;
}
void FOpenGLDebugFrameDumper::DumpBoundVertexArrayBufferContents( GLint VertexBufferID, GLint StartVertex, GLint VertexCount, GLint InstanceCount )
{
GLint BufferMapped;
glGetBufferParameteriv( GL_ARRAY_BUFFER, GL_BUFFER_MAPPED, &BufferMapped );
ASSERT_NO_GL_ERROR();
if( BufferMapped )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Can't map vertex array buffer %d for reading contents, as it's currently mapped!"), VertexBufferID );
return;
}
GLint64 TotalBufferSize = 0LL; // getters apparently like to overwrite just some bytes of this on Mac, if they return small number, so we need to put 0 in both dwords initially
glGetBufferParameteri64v( GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &TotalBufferSize );
ASSERT_NO_GL_ERROR();
GLuint BufferSize = (GLuint)TotalBufferSize; // yes, I know it's conversion to shorter variable, I don't expect buffers > 4GB in size.
void* BufferPtr = glMapBufferRange( GL_ARRAY_BUFFER, 0, BufferSize, GL_MAP_READ_BIT );
ASSERT_NO_GL_ERROR();
if( !BufferPtr )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Failed to map vertex array buffer %d!"), VertexBufferID );
return;
}
// Now gather information for this buffer from vertex attrib arrays
GLint MaxVertexAttribs = 0;
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &MaxVertexAttribs );
ASSERT_NO_GL_ERROR();
VertexAttribInfo* VertexAttribInfoTable = (VertexAttribInfo*)FMemory::Malloc( sizeof(VertexAttribInfo) * MaxVertexAttribs );
if( !VertexAttribInfoTable )
{
glUnmapBuffer( GL_ARRAY_BUFFER );
ASSERT_NO_GL_ERROR();
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Failed to allocate memory for vertex attrib info table!") );
return;
}
GLint CommonStrideForAllAttributes = -1;
GLint CommonDivisorForAllAttributes = -1;
bool bCanUseCommonStrideAndDivisor = true;
uint32 RelevantVertexAttribs = 0;
for( GLint VertexAttribIndex = 0; VertexAttribIndex < MaxVertexAttribs; ++VertexAttribIndex )
{
GLint VertexAttribArrayEnabled;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &VertexAttribArrayEnabled );
ASSERT_NO_GL_ERROR();
if( !VertexAttribArrayEnabled )
{
continue;
}
GLint VertexAttribArrayBufferBinding;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &VertexAttribArrayBufferBinding );
ASSERT_NO_GL_ERROR();
if( VertexAttribArrayBufferBinding != VertexBufferID )
{
continue;
}
bool bDifferentStrideOrDivisor = false;
VertexAttribInfoTable[RelevantVertexAttribs].Index = VertexAttribIndex;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_SIZE, &VertexAttribInfoTable[RelevantVertexAttribs].SizeRead );
ASSERT_NO_GL_ERROR();
VertexAttribInfoTable[RelevantVertexAttribs].Size = ( VertexAttribInfoTable[RelevantVertexAttribs].SizeRead != GL_BGRA ) ? VertexAttribInfoTable[RelevantVertexAttribs].SizeRead : 4;
check( VertexAttribInfoTable[RelevantVertexAttribs].Size <= 4 );
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_STRIDE, (GLint*)&VertexAttribInfoTable[RelevantVertexAttribs].Stride );
ASSERT_NO_GL_ERROR();
if( CommonStrideForAllAttributes == -1 )
{
CommonStrideForAllAttributes = VertexAttribInfoTable[RelevantVertexAttribs].Stride;
}
else if( VertexAttribInfoTable[RelevantVertexAttribs].Stride != CommonStrideForAllAttributes )
{
bDifferentStrideOrDivisor = true;
}
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_TYPE, &VertexAttribInfoTable[RelevantVertexAttribs].Type );
ASSERT_NO_GL_ERROR();
GLint VertexAttribArrayNormalized;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &VertexAttribArrayNormalized );
ASSERT_NO_GL_ERROR();
VertexAttribInfoTable[RelevantVertexAttribs].bNormalized = ( VertexAttribArrayNormalized != 0 );
GLvoid* VertexAttribArrayPointer;
glGetVertexAttribPointerv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER, &VertexAttribArrayPointer );
ASSERT_NO_GL_ERROR();
VertexAttribInfoTable[RelevantVertexAttribs].Offset = (GLuint)((GLuint64)VertexAttribArrayPointer & 0xFFFFFFFF);
GLint VertexAttribArrayInteger;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_INTEGER, &VertexAttribArrayInteger );
ASSERT_NO_GL_ERROR();
VertexAttribInfoTable[RelevantVertexAttribs].bInteger = ( VertexAttribArrayInteger != 0 );
GLint VertexAttribArrayDivisor;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB, &VertexAttribArrayDivisor );
ASSERT_NO_GL_ERROR();
if( ( VertexAttribArrayDivisor != 0 ) && ( VertexAttribArrayDivisor != 1 ) )
{
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Vertex array buffer %d has non-typical divisor: %d"), VertexBufferID, VertexAttribArrayDivisor );
}
VertexAttribInfoTable[RelevantVertexAttribs].bDivisor = ( VertexAttribArrayDivisor != 0 );
if( CommonDivisorForAllAttributes == -1 )
{
CommonDivisorForAllAttributes = VertexAttribInfoTable[RelevantVertexAttribs].bDivisor;
}
else if( VertexAttribInfoTable[RelevantVertexAttribs].bDivisor != ( CommonDivisorForAllAttributes != 0 ) )
{
bDifferentStrideOrDivisor = true;
}
if( bDifferentStrideOrDivisor && bCanUseCommonStrideAndDivisor )
{
bCanUseCommonStrideAndDivisor = false;
}
++RelevantVertexAttribs;
}
if( RelevantVertexAttribs == 0 )
{
glUnmapBuffer( GL_ARRAY_BUFFER );
ASSERT_NO_GL_ERROR();
FMemory::Free( VertexAttribInfoTable );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Vertex array buffer %d isn't bound to any vertex attribs, despite it being chosen."), VertexBufferID );
return;
}
// Sort vertex attribs info by increasing offset into vertex
qsort( VertexAttribInfoTable, RelevantVertexAttribs, sizeof( VertexAttribInfo ), qsort_compare_VertexAttribInfo );
// Adjust offset within vertex, assumming that one of those is at zero offset within vertex
{
GLuint BaseOffset = VertexAttribInfoTable[0].Offset;
for( uint32 VertexAttribIndex = 0; VertexAttribIndex < RelevantVertexAttribs; ++VertexAttribIndex )
{
VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex = VertexAttribInfoTable[VertexAttribIndex].Offset - BaseOffset;
}
}
// Determine if the buffer is indexed, or instanced
if( InstanceCount && ( CommonDivisorForAllAttributes == 1 ) )
{
// Instanced buffer
StartVertex = 0;
VertexCount = InstanceCount;
}
FString LogFileName = *CachedEventFolder / FString::Printf( TEXT("vertexArrayBuffer%d.log"), VertexBufferID );
FOutputDeviceFile LogFile( *LogFileName );
LogFile.SetAutoEmitLineTerminator( false );
LogFile.Log( LINE_TERMINATOR ); // to end "log start" line
// Output header info ( bufferID, stride, size, vertexCount )
LogFile.Logf( TEXT("Vertex buffer %d, size %u, start vertex for the draw within buffer %u, vertex count for the draw %d:") LINE_TERMINATOR, VertexBufferID, BufferSize, StartVertex, VertexCount );
if( !bCanUseCommonStrideAndDivisor )
{
LogFile.Log( TEXT("(different attributes of the same buffer are placed with different stride or divisor, so it's impossible to determine unused parts of vertex)") LINE_TERMINATOR );
}
LogFile.Log( TEXT("============================ VERTEX BUFFER INFO SET UP IN VERTEX ATTRIBS =======================================") LINE_TERMINATOR );
// Go through vertex attrib info and output offset, size, normalized, integer, type for each attrib.
// If stride differs, or attrib is integer, say it won't be logged
GLuint OffsetCovered = 0;
for( uint32 VertexAttribIndex = 0; VertexAttribIndex < RelevantVertexAttribs; ++VertexAttribIndex )
{
VertexAttribInfoTable[VertexAttribIndex].bSkip = false;
if( bCanUseCommonStrideAndDivisor )
{
if( OffsetCovered < VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex )
{
LogFile.Logf( TEXT("\tOffset: %d - %d unidentified bytes") LINE_TERMINATOR, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex - OffsetCovered );
}
else if( OffsetCovered > VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex )
{
LogFile.Logf( TEXT("\t\t%d BYTES ARE SHARED WITH THE FOLLOWING ATTRIBUTE!") LINE_TERMINATOR, OffsetCovered - VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Event %d, vertex array buffer %d, vertex attrib at offset %d using exact same data as another attribute!"), EventCounter, VertexBufferID, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
}
}
LogFile.Logf( TEXT("\tOffset: %d (in buffer: %d ), Size: %d, type: %s, stride: %d, normalized: %s") LINE_TERMINATOR, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex, VertexAttribInfoTable[VertexAttribIndex].Offset,
VertexAttribInfoTable[VertexAttribIndex].Size, NameOfType( VertexAttribInfoTable[VertexAttribIndex].Type ), VertexAttribInfoTable[VertexAttribIndex].Stride, VertexAttribInfoTable[VertexAttribIndex].bNormalized ? TEXT("Yes") : TEXT("No") );
if( VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex > VertexAttribInfoTable[VertexAttribIndex].Stride )
{
LogFile.Log( TEXT("\t\tTHIS ATTRIBUTE STARTS BEYOND THE END OF VERTEX DATA! IT WILL BE SKIPPED.") LINE_TERMINATOR );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Event %d, ertex array buffer %d, vertex attrib at offset %d starts beyond end of vertex data!"), EventCounter, VertexBufferID, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
VertexAttribInfoTable[VertexAttribIndex].bSkip = true;
}
int32 SizeOfMember = VertexAttribInfoTable[VertexAttribIndex].Size * SizeOfType( VertexAttribInfoTable[VertexAttribIndex].Type );
OffsetCovered = VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex + SizeOfMember;
if( OffsetCovered > VertexAttribInfoTable[VertexAttribIndex].Stride )
{
LogFile.Log( TEXT("\t\tTHIS ATTRIBUTE ENDS BEYOND THE END OF VERTEX DATA! IT WILL BE SKIPPED.") LINE_TERMINATOR );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Event %d, vertex array buffer %d, vertex attrib at offset %d ends beyond end of vertex data!"), EventCounter, VertexBufferID, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
VertexAttribInfoTable[VertexAttribIndex].bSkip = true;
}
if( VertexAttribInfoTable[VertexAttribIndex].Offset + StartVertex * VertexAttribInfoTable[VertexAttribIndex].Stride > BufferSize )
{
LogFile.Log( TEXT("\t\tVALUES FROM THIS ATTRIBUTE SUBMITTED FOR OPENGL TO DRAW START BEYOND BUFFER END! IT WILL BE SKIPPED.") LINE_TERMINATOR );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Event %d, vertex array buffer %d, vertex attrib at offset %d - values from it submitted for OpenGL to draw start beyond buffer end!"), EventCounter, VertexBufferID, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
VertexAttribInfoTable[VertexAttribIndex].bSkip = true;
}
else if( VertexAttribInfoTable[VertexAttribIndex].Offset + ( StartVertex + VertexCount - 1 ) * VertexAttribInfoTable[VertexAttribIndex].Stride + SizeOfMember > BufferSize )
{
LogFile.Log( TEXT("\t\tVALUES FROM THIS ATTRIBUTE SUBMITTED FOR OPENGL TO DRAW EXTEND BEYOND BUFFER END! IT WILL BE SKIPPED.") LINE_TERMINATOR );
UE_LOG( LogRHI, Warning, TEXT("DEBUG FRAME DUMPER: Event %d, vertex array buffer %d, vertex attrib at offset %d - values from it submitted for OpenGL to draw extend beyond buffer end!"), EventCounter, VertexBufferID, VertexAttribInfoTable[VertexAttribIndex].OffsetWithinVertex );
VertexAttribInfoTable[VertexAttribIndex].bSkip = true;
}
}
LogFile.Log( TEXT("================================= INTERPRETED VERTEX BUFFER CONTENTS ===========================================") LINE_TERMINATOR );
// For each vertex, prepare a line with contributions from each attrib, and output it
FString Line;
for( int32 VertexIndex = 0; VertexIndex < VertexCount; ++VertexIndex )
{
Line = TEXT("");
for( uint32 VertexAttribIndex = 0; VertexAttribIndex < RelevantVertexAttribs; ++VertexAttribIndex )
{
if( VertexAttribInfoTable[VertexAttribIndex].bSkip )
{
continue;
}
if( Line.Len() )
{
Line += TEXT(", ");
}
else
{
Line = FString::Printf( TEXT("%08d: "), VertexIndex );
}
uint32 Offset = VertexAttribInfoTable[VertexAttribIndex].Offset + ( StartVertex + VertexIndex ) * VertexAttribInfoTable[VertexAttribIndex].Stride;
int32 SizeOfMember = VertexAttribInfoTable[VertexAttribIndex].Size * SizeOfType( VertexAttribInfoTable[VertexAttribIndex].Type );
if( Offset + SizeOfMember > BufferSize )
{
Line += TEXT("(beyond end of buffer)");
}
else
{
const uint8* ValuePtr = (const uint8*)BufferPtr + Offset;
switch( VertexAttribInfoTable[VertexAttribIndex].Type )
{
case GL_FLOAT:
{
const float* FloatPtr = (const float*)ValuePtr;
if( VertexAttribInfoTable[VertexAttribIndex].Size == 1 )
{
Line += FString::Printf( TEXT("%f"), FloatPtr[0] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 2 )
{
Line += FString::Printf( TEXT("{ %f, %f }"), FloatPtr[0], FloatPtr[1] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 3 )
{
Line += FString::Printf( TEXT("{ %f, %f, %f }"), FloatPtr[0], FloatPtr[1], FloatPtr[2] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 4 )
{
Line += FString::Printf( TEXT("{ %f, %f, %f, %f }"), FloatPtr[0], FloatPtr[1], FloatPtr[2], FloatPtr[3] );
}
else
{
Line += TEXT("(unhandled float count)");
}
}
break;
case GL_UNSIGNED_BYTE:
{
const uint8* Uint8Ptr = (const uint8*)ValuePtr;
if( VertexAttribInfoTable[VertexAttribIndex].Size == 1 )
{
Line += FString::Printf( TEXT("%u"), Uint8Ptr[0] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 2 )
{
Line += FString::Printf( TEXT("{ %u, %u }"), Uint8Ptr[0], Uint8Ptr[1] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 3 )
{
Line += FString::Printf( TEXT("{ %u, %u, %u }"), Uint8Ptr[0], Uint8Ptr[1], Uint8Ptr[2] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 4 )
{
Line += FString::Printf( TEXT("{ %u, %u, %u, %u }"), Uint8Ptr[0], Uint8Ptr[1], Uint8Ptr[2], Uint8Ptr[3] );
}
else
{
Line += TEXT("(unhandled unsigned char count)");
}
}
break;
case GL_UNSIGNED_SHORT:
{
const uint16* UInt16Ptr = (const uint16*)ValuePtr;
if( VertexAttribInfoTable[VertexAttribIndex].Size == 1 )
{
Line += FString::Printf( TEXT("%u"), UInt16Ptr[0] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 2 )
{
Line += FString::Printf( TEXT("{ %u, %u }"), UInt16Ptr[0], UInt16Ptr[1] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 3 )
{
Line += FString::Printf( TEXT("{ %u, %u, %u }"), UInt16Ptr[0], UInt16Ptr[1], UInt16Ptr[2] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 4 )
{
Line += FString::Printf( TEXT("{ %u, %u, %u, %u }"), UInt16Ptr[0], UInt16Ptr[1], UInt16Ptr[2], UInt16Ptr[3] );
}
else
{
Line += TEXT("(unhandled unsigned short count)");
}
}
break;
case GL_SHORT:
{
const int16* Int16Ptr = (const int16*)ValuePtr;
if( VertexAttribInfoTable[VertexAttribIndex].Size == 1 )
{
Line += FString::Printf( TEXT("%d"), Int16Ptr[0] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 2 )
{
Line += FString::Printf( TEXT("{ %d, %d }"), Int16Ptr[0], Int16Ptr[1] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 3 )
{
Line += FString::Printf( TEXT("{ %d, %d, %d }"), Int16Ptr[0], Int16Ptr[1], Int16Ptr[2] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 4 )
{
Line += FString::Printf( TEXT("{ %d, %d, %d, %d }"), Int16Ptr[0], Int16Ptr[1], Int16Ptr[2], Int16Ptr[3] );
}
else
{
Line += TEXT("(unhandled short count)");
}
}
break;
case GL_HALF_FLOAT:
if( VertexAttribInfoTable[VertexAttribIndex].Size > 4 )
{
Line += TEXT("(unhandled float count)");
}
else
{
float Floats[4];
const uint16* UInt16Ptr = (const uint16*)ValuePtr;
for( int32 MemberIndex = 0; MemberIndex < VertexAttribInfoTable[VertexAttribIndex].Size; ++MemberIndex )
Floats[MemberIndex] = HalfFloatToFloat( UInt16Ptr[MemberIndex] );
if( VertexAttribInfoTable[VertexAttribIndex].Size == 1 )
{
Line += FString::Printf( TEXT("%f"), Floats[0] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 2 )
{
Line += FString::Printf( TEXT("{ %f, %f }"), Floats[0], Floats[1] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 3 )
{
Line += FString::Printf( TEXT("{ %f, %f, %f }"), Floats[0], Floats[1], Floats[2] );
}
else if( VertexAttribInfoTable[VertexAttribIndex].Size == 4 )
{
Line += FString::Printf( TEXT("{ %f, %f, %f, %f }"), Floats[0], Floats[1], Floats[2], Floats[3] );
}
else
{
Line += TEXT("(unhandled float count)");
}
}
break;
default:
Line += TEXT("(unhandled type)");
break;
}
}
}
if( Line.Len() )
{
Line += LINE_TERMINATOR;
LogFile.Log( *Line );
}
}
LogFile.Log( TEXT("================================================================================================================") LINE_TERMINATOR );
// Clean up
LogFile.TearDown();
glUnmapBuffer( GL_ARRAY_BUFFER );
ASSERT_NO_GL_ERROR();
FMemory::Free( VertexAttribInfoTable );
}
void FOpenGLDebugFrameDumper::DumpRelevantVertexArrayBufferContents( GLint StartVertex, GLint VertexCount, GLint InstanceCount )
{
GLint MaxVertexAttribs = 0;
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &MaxVertexAttribs );
ASSERT_NO_GL_ERROR();
GLint IndicesToDump[64];
GLint AttribsToDump[64];
GLint DumpCount = 0;
for( GLint VertexAttribIndex = 0; VertexAttribIndex < MaxVertexAttribs; ++VertexAttribIndex )
{
GLint VertexAttribArrayEnabled;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &VertexAttribArrayEnabled );
ASSERT_NO_GL_ERROR();
if( !VertexAttribArrayEnabled )
{
continue;
}
GLint VertexAttribArrayBufferBinding;
glGetVertexAttribiv( VertexAttribIndex, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &VertexAttribArrayBufferBinding );
ASSERT_NO_GL_ERROR();
bool bIsFound = false;
for( GLint AlreadyThereIndex = 0; AlreadyThereIndex < DumpCount; ++AlreadyThereIndex )
{
if( IndicesToDump[AlreadyThereIndex] == VertexAttribArrayBufferBinding )
{
bIsFound = true;
break;
}
}
if( bIsFound )
{
continue;
}
IndicesToDump[DumpCount] = VertexAttribArrayBufferBinding;
AttribsToDump[DumpCount] = VertexAttribIndex;
++DumpCount;
check( DumpCount < 64 );
}
if( DumpCount )
{
GLint PreviousVertexBuffer;
glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &PreviousVertexBuffer );
ASSERT_NO_GL_ERROR();
GLint CurrentVertexBuffer = PreviousVertexBuffer;
for( GLint ArrayToDumpIndex = 0; ArrayToDumpIndex < DumpCount; ++ArrayToDumpIndex )
{
GLint VertexBufferID = IndicesToDump[ArrayToDumpIndex];
if( VertexBufferID != CurrentVertexBuffer )
{
glBindBuffer( GL_ARRAY_BUFFER, VertexBufferID );
ASSERT_NO_GL_ERROR();
CurrentVertexBuffer = VertexBufferID;
}
DumpBoundVertexArrayBufferContents( VertexBufferID, StartVertex, VertexCount, InstanceCount );
}
if( CurrentVertexBuffer != PreviousVertexBuffer )
{
glBindBuffer( GL_ARRAY_BUFFER, PreviousVertexBuffer );
ASSERT_NO_GL_ERROR();
}
}
}
void FOpenGLDebugFrameDumper::TriggerFrameDump( void )
{
if( bDumpingFrame )
return;
if( CachedRootFolder == NULL )
{
// Delete entire root frame dump folder with everything in it, if it exists.
CachedRootFolder = new FString( FPaths::ProfilingDir() / TEXT("OpenGLDebugFrameDump") );
IFileManager::Get().DeleteDirectory( **CachedRootFolder, false, true );
// Create new root frame dump folder
IFileManager::Get().MakeDirectory( **CachedRootFolder );
}
// Create new frame folder
if( CachedFrameFolder )
delete CachedFrameFolder;
CachedFrameFolder = new FString( *CachedRootFolder / FString::Printf( TEXT( "Frame_%08u" ), FrameCounter ) );
UE_LOG( LogRHI, Log, TEXT("DEBUG FRAME DUMPER: Frame %d dump started."), FrameCounter );
EventCounter = 0;
bDumpingFrame = true;
}
void FOpenGLDebugFrameDumper::SetNewEventFolder( const FString& EventString )
{
// Create new event folder
if( CachedEventFolder )
delete CachedEventFolder;
CachedEventFolder = new FString( *CachedFrameFolder / FString::Printf( TEXT( "Event_%08u-" ), EventCounter ) + EventString );
}
void FOpenGLDebugFrameDumper::SignalDrawEvent( const TCHAR* FolderPart, const TCHAR* DrawCommandDescription, GLint ElementArrayType, GLint StartVertex, GLint VertexCount, GLint InstanceCount )
{
if( !bDumpingFrame )
return;
// if( ![NSOpenGLContext currentContext] )
// {
// UE_LOG(LogRHI, Log, TEXT("DEBUG FRAME DUMPER: No valid OpenGL context set in calling thread!"));
// return;
// }
SetNewEventFolder( FolderPart );
DumpGeneralOpenGLState( DrawCommandDescription, true, false );
DumpFramebufferState( false );
DumpProgramAndShaderState();
DumpBoundTextureState();
DumpFramebufferContents( false );
DumpBoundTexturesContents();
if( ElementArrayType != 0 )
{
DumpElementArrayBufferContents( ElementArrayType );
}
DumpRelevantVertexArrayBufferContents( StartVertex, VertexCount, InstanceCount );
// TO DO - dump the rest of OpenGL state
++EventCounter;
}
void FOpenGLDebugFrameDumper::SignalClearEvent( int8 ClearType, int8 NumColors, const float* Colors, float Depth, uint32 Stencil )
{
if( !bDumpingFrame )
return;
// if( ![NSOpenGLContext currentContext] )
// {
// UE_LOG(LogRHI, Log, TEXT("DEBUG FRAME DUMPER: No valid OpenGL context set in calling thread!"));
// return;
// }
SetNewEventFolder( TEXT("glClearBuffer(s)") );
FString MaskString;
{
bool bHasText = false;
MaskString = TEXT("");
if( ClearType & 4 )
{
MaskString = FString::Printf( TEXT( "%d color buffers( " ), NumColors );
for( int32 ColorIndex = 0; ColorIndex < NumColors; ++ColorIndex )
{
if( ColorIndex )
{
MaskString += TEXT(", ");
}
MaskString += *FString::Printf( TEXT("(%f,%f,%f,%f)"), Colors[4*ColorIndex], Colors[4*ColorIndex+1], Colors[4*ColorIndex+2], Colors[4*ColorIndex+3] );
}
MaskString += TEXT(" )");
bHasText = true;
}
if( ClearType & 1 )
{
if( bHasText )
{
MaskString += TEXT(", ");
}
MaskString += *FString::Printf( TEXT( "depth(%f)" ), Depth );
bHasText = true;
}
if( ClearType & 2 )
{
if( bHasText )
{
MaskString += TEXT(", ");
}
MaskString += *FString::Printf( TEXT( "stencil(0x%x)" ), Stencil );
}
}
FString DrawCommandDescription = FString( TEXT("glClearBuffer*( ") ) + MaskString + TEXT(" )");
DumpGeneralOpenGLState( *DrawCommandDescription, false, false );
DumpFramebufferState( false );
DumpFramebufferContents( false );
++EventCounter;
}
void FOpenGLDebugFrameDumper::SignalFramebufferBlitEvent( GLbitfield Mask )
{
if( !bDumpingFrame )
return;
// if( ![NSOpenGLContext currentContext] )
// {
// UE_LOG(LogRHI, Log, TEXT("DEBUG FRAME DUMPER: No valid OpenGL context set in calling thread!"));
// return;
// }
SetNewEventFolder( TEXT("glFramebufferBlit") );
FString MaskString;
{
MaskString = ( Mask & GL_COLOR_BUFFER_BIT ) ? TEXT( "GL_COLOR_BUFFER_BIT" ) : TEXT("");
bool bHasText = ( ( Mask & GL_COLOR_BUFFER_BIT ) != 0 );
if( Mask & GL_DEPTH_BUFFER_BIT )
{
if( bHasText )
MaskString += TEXT("|");
MaskString += TEXT( "GL_DEPTH_BUFFER_BIT" );
bHasText = true;
}
if( Mask & GL_STENCIL_BUFFER_BIT )
{
if( bHasText )
MaskString += TEXT("|");
MaskString += TEXT( "GL_STENCIL_BUFFER_BIT" );
}
}
FString DrawCommandDescription = FString( TEXT("glFramebufferBlit(") ) + MaskString + TEXT(")");
DumpGeneralOpenGLState( *DrawCommandDescription, false, true );
DumpFramebufferState( false );
DumpFramebufferState( true );
DumpFramebufferContents( false );
DumpFramebufferContents( true );
++EventCounter;
}
void FOpenGLDebugFrameDumper::SignalEndFrameEvent( void )
{
if( !bDumpingFrame )
return;
// if( ![NSOpenGLContext currentContext] )
// {
// UE_LOG(LogRHI, Log, TEXT("DEBUG FRAME DUMPER: No valid OpenGL context set in calling thread!"));
// return;
// }
SetNewEventFolder( TEXT("BufferFlush") );
DumpGeneralOpenGLState( TEXT("(BufferFlush)"), false, false );
DumpFramebufferContents( false );
// TO DO - dump the rest of OpenGL state
UE_LOG( LogRHI, Log, TEXT("DEBUG FRAME DUMPER: Frame %d dump ended."), FrameCounter );
bDumpingFrame = false;
EventCounter = 0;
++FrameCounter;
}
/*=============================================================================
Implementation of C methods that serve as the only connection all external code may depend on.
=============================================================================*/
static const TCHAR* GetPrimitiveTypeString( GLint type )
{
switch( type )
{
case GL_TRIANGLES: return TEXT("GL_TRIANGLES");
case GL_POINTS: return TEXT("GL_POINTS");
case GL_LINES: return TEXT("GL_LINES");
case GL_LINE_STRIP: return TEXT("GL_LINE_STRIP");
case GL_TRIANGLE_STRIP: return TEXT("GL_TRIANGLE_STRIP");
case GL_TRIANGLE_FAN: return TEXT("GL_TRIANGLE_FAN");
default: return TEXT("!!!unknown!!!");
}
}
extern "C" {
void SignalOpenGLDrawArraysEvent( GLenum Mode, GLint First, GLsizei Count )
{
FOpenGLDebugFrameDumper::Instance()->SignalDrawEvent(
TEXT("glDrawArrays"),
*FString::Printf( TEXT( "glDrawArrays( Mode = %s, First = %d, Count = %u )" ),
GetPrimitiveTypeString( Mode ),
First,
Count
),
0, // no index buffer
First,
Count,
0
);
}
void SignalOpenGLDrawArraysInstancedEvent( GLenum Mode, GLint First, GLsizei Count, GLsizei PrimCount )
{
FOpenGLDebugFrameDumper::Instance()->SignalDrawEvent(
TEXT("glDrawArraysInstanced"),
*FString::Printf(
TEXT( "glDrawArraysInstanced( Mode = %s, First = %d, Count = %u, PrimCount = %u )" ),
GetPrimitiveTypeString( Mode ),
First,
Count,
PrimCount
),
0, // no index buffer
First,
Count,
PrimCount
);
}
void SignalOpenGLDrawRangeElementsEvent( GLenum Mode, GLuint Start, GLuint End, GLsizei Count, GLenum Type, const GLvoid* Indices )
{
FOpenGLDebugFrameDumper::Instance()->SignalDrawEvent(
TEXT("glDrawRangeElements"),
*FString::Printf(
TEXT( "glDrawRangeElements( Mode = %s, Start = %d, End = %d, Count = %u, Type = %s, Indices = %p )" ),
GetPrimitiveTypeString( Mode ),
Start,
End,
Count,
( ( Type == GL_UNSIGNED_INT ) ? TEXT("GL_UNSIGNED_INT") : TEXT("GL_UNSIGNED_SHORT") ),
Indices
),
(GLint)Type,
Start,
End - Start,
0
);
}
void SignalOpenGLDrawRangeElementsInstancedEvent( GLenum Mode, GLsizei Count, GLenum Type, const GLvoid* Indices, GLsizei PrimCount )
{
FOpenGLDebugFrameDumper::Instance()->SignalDrawEvent(
TEXT("glDrawElementsInstanced"),
*FString::Printf(
TEXT( "glDrawElementsInstanced( Mode = %s, Count = %u, Type = %s, Indices = %p, PrimCount = %u )" ),
GetPrimitiveTypeString( Mode ),
Count,
( ( Type == GL_UNSIGNED_INT ) ? TEXT("GL_UNSIGNED_INT") : TEXT("GL_UNSIGNED_SHORT") ),
Indices,
PrimCount
),
(GLint)Type,
0,
Count,
PrimCount
);
}
void SignalOpenGLClearEvent( int8 ClearType, int8 NumColors, const float* Colors, float Depth, uint32 Stencil )
{
FOpenGLDebugFrameDumper::Instance()->SignalClearEvent( ClearType, NumColors, Colors, Depth, Stencil );
}
void SignalOpenGLFramebufferBlitEvent( GLbitfield Mask )
{
FOpenGLDebugFrameDumper::Instance()->SignalFramebufferBlitEvent( Mask );
}
void SignalOpenGLEndFrameEvent( void )
{
FOpenGLDebugFrameDumper::Instance()->SignalEndFrameEvent();
}
void TriggerOpenGLFrameDump( void )
{
FOpenGLDebugFrameDumper::Instance()->TriggerFrameDump();
}
void TriggerOpenGLFrameDumpEveryXCalls( int32 X )
{
static int32 Counter = 0;
if( Counter >= X )
{
FOpenGLDebugFrameDumper::Instance()->TriggerFrameDump();
Counter = 0;
}
++Counter;
}
} // extern "C"
#endif // ENABLE_OPENGL_FRAMEDUMP