You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#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 Change3303824on 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 Change3311855on 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 Change3347562on 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 Change3352065on 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 Change3352967on 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. Change3355631on 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]
5597 lines
194 KiB
C++
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
|