From 10286b74bb3b921325264eb4f3110e49caf1e0be Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 27 Nov 2015 17:33:50 +0100 Subject: [PATCH 001/102] Bug 1221913 - Make swiping work correctly in e10s mode even if APZ is off. r=kats --HG-- extra : commitid : 6jMrb8f9Atw extra : amend_source : 6cffb6ab209c3f55739ddb409b1a4d04d31a159a --- dom/ipc/TabChild.cpp | 7 ++++--- dom/ipc/TabParent.cpp | 10 ++++++---- widget/cocoa/nsChildView.mm | 27 ++++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index b921773be1a4..2aea815af544 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1808,10 +1808,11 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent, event.widget = mPuppetWidget; APZCCallbackHelper::DispatchWidgetEvent(event); + if (event.mCanTriggerSwipe) { + SendRespondStartSwipeEvent(aInputBlockId, event.TriggersSwipe()); + } + if (aEvent.mFlags.mHandledByAPZ) { - if (event.mCanTriggerSwipe) { - SendRespondStartSwipeEvent(aInputBlockId, event.TriggersSwipe()); - } mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId); } return true; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 22cb3613b3b0..c6e2146a6d82 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2694,6 +2694,12 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutInputBlockId, nsEventStatus* aOutApzResponse) { + // Let the widget know that the event will be sent to the child process, + // which will (hopefully) send a confirmation notice back to APZ. + // Do this even if APZ is off since we need it for swipe gesture support on + // OS X without APZ. + InputAPZContext::SetRoutedToChildProcess(); + if (AsyncPanZoomEnabled()) { if (aOutTargetGuid) { *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid(); @@ -2715,10 +2721,6 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid, if (aOutApzResponse) { *aOutApzResponse = InputAPZContext::GetApzResponse(); } - - // Let the widget know that the event will be sent to the child process, - // which will (hopefully) send a confirmation notice back to APZ. - InputAPZContext::SetRoutedToChildProcess(); } else { if (aOutInputBlockId) { *aOutInputBlockId = 0; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index edd2503b1928..1643bc8a7499 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -65,6 +65,7 @@ #include "mozilla/layers/CompositorOGL.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/BasicCompositor.h" +#include "mozilla/layers/InputAPZContext.h" #include "gfxUtils.h" #include "gfxPrefs.h" #include "mozilla/gfx/2D.h" @@ -2616,6 +2617,10 @@ nsChildView::TrackScrollEventAsSwipe(const mozilla::PanGestureInput& aSwipeStart mSwipeTracker = new SwipeTracker(*this, aSwipeStartEvent, aAllowedDirections, direction); + + if (!mAPZC) { + mCurrentPanGestureBelongsToSwipe = true; + } } void @@ -2826,11 +2831,27 @@ nsChildView::DispatchAPZWheelInputEvent(InputData& aEvent, bool aCanTriggerSwipe event = panInput.ToWidgetWheelEvent(this); if (aCanTriggerSwipe) { SwipeInfo swipeInfo = SendMayStartSwipe(panInput); + + // We're in the non-APZ case here, but we still want to know whether + // the event was routed to a child process, so we use InputAPZContext + // to get that piece of information. + ScrollableLayerGuid guid; + InputAPZContext context(guid, 0, nsEventStatus_eIgnore); + event.mCanTriggerSwipe = swipeInfo.wantsSwipe; DispatchEvent(&event, status); - if (event.TriggersSwipe()) { - TrackScrollEventAsSwipe(panInput, swipeInfo.allowedDirections); - mCurrentPanGestureBelongsToSwipe = true; + if (swipeInfo.wantsSwipe) { + if (context.WasRoutedToChildProcess()) { + // We don't know whether this event can start a swipe, so we need + // to queue up events and wait for a call to ReportSwipeStarted. + mSwipeEventQueue = MakeUnique(swipeInfo.allowedDirections, 0); + } else if (event.TriggersSwipe()) { + TrackScrollEventAsSwipe(panInput, swipeInfo.allowedDirections); + } + } + + if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == 0) { + mSwipeEventQueue->queuedEvents.AppendElement(panInput); } return; } From 9bdb4a34ae9b6d63614ec89ea33e809ae8a59c30 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 27 Nov 2015 16:09:18 +0100 Subject: [PATCH 002/102] Bug 1198021 - When the ContentClient of a ClientTiledPaintedLayer changes, don't destroy the old content client until after the layer transaction has been sent. r=nical --HG-- extra : commitid : EU0rpj0WygC extra : amend_source : 4c68b28a13dbf4220c31880bb504284c84a3b98d --- gfx/layers/client/ClientLayerManager.cpp | 1 + gfx/layers/client/ClientTiledPaintedLayer.cpp | 5 +++-- gfx/layers/ipc/CompositableForwarder.h | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 413cc35aba07..8f825629ac3f 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -636,6 +636,7 @@ ClientLayerManager::ForwardTransaction(bool aScheduleComposite) } mForwarder->RemoveTexturesIfNecessary(); + mForwarder->RemoveCompositablesIfNecessary(); mForwarder->SendPendingAsyncMessges(); mPhase = PHASE_NONE; diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp index ef0d67838166..13790d8ffbbe 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.cpp +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -412,8 +412,9 @@ ClientTiledPaintedLayer::RenderLayer() IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size(); if (mContentClient && !mContentClient->SupportsLayerSize(layerSize, ClientManager())) { - ClearCachedResources(); - MOZ_ASSERT(!mContentClient); + ClientManager()->AsShadowForwarder()->HoldUntilTransaction(mContentClient); + mContentClient = nullptr; + mValidRegion.SetEmpty(); } if (!mContentClient) { diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h index cfda9fd2e85c..ac83da3a4174 100644 --- a/gfx/layers/ipc/CompositableForwarder.h +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -10,6 +10,7 @@ #include // for int32_t, uint64_t #include "gfxTypes.h" #include "mozilla/Attributes.h" // for override +#include "mozilla/layers/CompositableClient.h" // for CompositableClient #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/LayersTypes.h" // for LayersBackend @@ -127,6 +128,20 @@ public: mTexturesToRemove.Clear(); } + /** + * The same as above, but for CompositableClients. + */ + void HoldUntilTransaction(CompositableClient* aClient) + { + if (aClient) { + mCompositableClientsToRemove.AppendElement(aClient); + } + } + void RemoveCompositablesIfNecessary() + { + mCompositableClientsToRemove.Clear(); + } + struct TimedTextureClient { TimedTextureClient() : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {} @@ -190,6 +205,7 @@ public: protected: TextureFactoryIdentifier mTextureFactoryIdentifier; nsTArray > mTexturesToRemove; + nsTArray> mCompositableClientsToRemove; RefPtr mSyncObject; const int32_t mSerial; static mozilla::Atomic sSerialCounter; From b903af89fca38185dbc9684b285ad6e2f59e1461 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Tue, 1 Dec 2015 16:41:08 +0100 Subject: [PATCH 003/102] Bug 1072501: Unmap file mapping on source surface destruction. r=milan --- gfx/layers/TextureDIB.cpp | 17 ++++++++++++----- gfx/layers/client/TextureClient.cpp | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 7dcaf3c21439..48e68cd72185 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -230,10 +230,9 @@ ShmemDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, } uint8_t* data = (uint8_t*)::MapViewOfFile(fileMapping, FILE_MAP_WRITE | FILE_MAP_READ, - 0, 0, aSize.width * aSize.height - * BytesPerPixel(aFormat)); + 0, 0, mapSize); - memset(data, 0x80, aSize.width * aSize.height * BytesPerPixel(aFormat)); + memset(data, 0x80, mapSize); ::UnmapViewOfFile(fileMapping); @@ -387,6 +386,14 @@ TextureHostFileMapping::~TextureHostFileMapping() ::CloseHandle(mFileMapping); } +UserDataKey kFileMappingKey; + +static void UnmapFileData(void* aData) +{ + MOZ_ASSERT(aData); + ::UnmapViewOfFile(aData); +} + void TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) { @@ -405,14 +412,14 @@ TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) if (data) { RefPtr surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat); + surf->AddUserData(&kFileMappingKey, data, UnmapFileData); + if (!mTextureSource->Update(surf, const_cast(aRegion))) { mTextureSource = nullptr; } } else { mTextureSource = nullptr; } - - ::UnmapViewOfFile(data); } } diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index 0dfc8afefd06..a5a60d61f927 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -728,7 +728,6 @@ TextureClient::CreateForDrawing(CompositableForwarder* aAllocator, } if (!data && aFormat == SurfaceFormat::B8G8R8X8 && - aAllocator->IsSameProcess() && moz2DBackend == gfx::BackendType::CAIRO && NS_IsMainThread()) { data = DIBTextureData::Create(aSize, aFormat, aAllocator); From ca3bff1132f19244d2e96a713da1d1e66713a170 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 1 Dec 2015 12:01:56 -0500 Subject: [PATCH 004/102] Bug 1228876. Correctly ignore nested optgroups even if an optgroup is inserted into another, existing, optgroup. r=mats --- dom/html/HTMLSelectElement.cpp | 18 ++++++++---------- dom/html/crashtests/1228876.html | 21 +++++++++++++++++++++ dom/html/crashtests/crashtests.list | 3 ++- 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 dom/html/crashtests/1228876.html diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index e8f042a98135..07003287b06f 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -225,18 +225,17 @@ HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, int32_t aDepth, bool aNotify) { + MOZ_ASSERT(aDepth == 0 || aDepth == 1); int32_t insertIndex = aListIndex; HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions); if (optElement) { mOptions->InsertOptionAt(optElement, insertIndex); insertIndex++; - } else { + } else if (aDepth == 0) { // If it's at the top level, then we just found out there are non-options // at the top level, which will throw off the insert count - if (aDepth == 0) { - mNonOptionChildren++; - } + mNonOptionChildren++; // Deal with optgroups if (aOptions->IsHTMLElement(nsGkAtoms::optgroup)) { @@ -252,7 +251,7 @@ HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, } } } - } + } // else ignore even if optgroup; we want to ignore nested optgroups. // Deal with the selected list if (insertIndex - aListIndex) { @@ -307,6 +306,7 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, int32_t aDepth, bool aNotify) { + MOZ_ASSERT(aDepth == 0 || aDepth == 1); int32_t numRemoved = 0; HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions); @@ -317,11 +317,9 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, } mOptions->RemoveOptionAt(aListIndex); numRemoved++; - } else { + } else if (aDepth == 0) { // Yay, one less artifact at the top level. - if (aDepth == 0) { - mNonOptionChildren--; - } + mNonOptionChildren--; // Recurse down deeper for options if (mOptGroupCount && aOptions->IsHTMLElement(nsGkAtoms::optgroup)) { @@ -341,7 +339,7 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, } } } - } + } // else don't check for an optgroup; we want to ignore nested optgroups if (numRemoved) { // Tell the widget we removed the options diff --git a/dom/html/crashtests/1228876.html b/dom/html/crashtests/1228876.html new file mode 100644 index 000000000000..b7beb645cfe2 --- /dev/null +++ b/dom/html/crashtests/1228876.html @@ -0,0 +1,21 @@ + + + + + + + diff --git a/dom/html/crashtests/crashtests.list b/dom/html/crashtests/crashtests.list index 8942ed3e5bbc..e48b5d440ceb 100644 --- a/dom/html/crashtests/crashtests.list +++ b/dom/html/crashtests/crashtests.list @@ -73,4 +73,5 @@ load 903106.html load 916322-1.html load 916322-2.html load 1032654.html -pref(dom.image.srcset.enabled,true) load 1141260.html \ No newline at end of file +pref(dom.image.srcset.enabled,true) load 1141260.html +load 1228876.html From 09e35c652abe6296f9f91b6c325382bac12aed22 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 1 Dec 2015 12:02:33 -0500 Subject: [PATCH 005/102] Bug 1048695 part 1. Pass the set of globals where a member should NOT be exposed to MemberCondition. r=peterv --- dom/bindings/Codegen.py | 83 +++++++++++++++++++++++++++++---------- dom/bindings/DOMJSClass.h | 14 +++++++ 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 050843d7d55d..20e775a23b96 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1906,16 +1906,26 @@ def getAvailableInTestFunc(obj): class MemberCondition: """ An object representing the condition for a member to actually be - exposed. Any of pref, func, and available can be None. If not - None, they should be strings that have the pref name (for "pref") - or function name (for "func" and "available"). + exposed. Any of the arguments can be None. If not + None, they should have the following types: + + pref: The name of the preference. + func: The name of the function. + available: A string indicating where we should be available. + checkAnyPermissions: An integer index for the anypermissions_* to use. + checkAllPermissions: An integer index for the allpermissions_* to use. + nonExposedGlobals: A set of names of globals. Can be empty, in which case + it's treated the same way as None. """ - def __init__(self, pref, func, available=None, checkAnyPermissions=None, checkAllPermissions=None): + def __init__(self, pref=None, func=None, available=None, + checkAnyPermissions=None, checkAllPermissions=None, + nonExposedGlobals=None): assert pref is None or isinstance(pref, str) assert func is None or isinstance(func, str) assert available is None or isinstance(available, str) assert checkAnyPermissions is None or isinstance(checkAnyPermissions, int) assert checkAllPermissions is None or isinstance(checkAllPermissions, int) + assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set) self.pref = pref def toFuncPtr(val): @@ -1933,11 +1943,20 @@ class MemberCondition: else: self.checkAllPermissions = "allpermissions_%i" % checkAllPermissions + if nonExposedGlobals: + # Nonempty set + self.nonExposedGlobals = " | ".join( + map(lambda g: "GlobalNames::%s" % g, + sorted(nonExposedGlobals))) + else: + self.nonExposedGlobals = "0" + def __eq__(self, other): return (self.pref == other.pref and self.func == other.func and self.available == other.available and self.checkAnyPermissions == other.checkAnyPermissions and - self.checkAllPermissions == other.checkAllPermissions) + self.checkAllPermissions == other.checkAllPermissions and + self.nonExposedGlobals == other.nonExposedGlobals) def __ne__(self, other): return not self.__eq__(other) @@ -2000,13 +2019,34 @@ class PropertyDefiner: @staticmethod def getControllingCondition(interfaceMember, descriptor): - return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember, - "Pref"), - PropertyDefiner.getStringAttr(interfaceMember, - "Func"), - getAvailableInTestFunc(interfaceMember), - descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name), - descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name)) + # We do a slightly complicated thing for exposure sets to deal nicely + # with the situation of an [Exposed=Window] thing on an interface + # exposed in workers that has a worker-specific descriptor. In that + # situation, we already skip generation of the member entirely in the + # worker binding, and shouldn't need to check for the various worker + # scopes in the non-worker binding. + interface = descriptor.interface + nonExposureSet = interface.exposureSet - interfaceMember.exposureSet + # Skip getting the descriptor if we're just exposed everywhere or not + # looking at the non-worker descriptor. + if len(nonExposureSet) and not descriptor.workers: + workerProvider = descriptor.config.getDescriptorProvider(True) + workerDesc = workerProvider.getDescriptor(interface.identifier.name) + if workerDesc.workers: + # Just drop all the worker interface names from the + # nonExposureSet, since we know we'll have a mainthread global + # of some sort. + nonExposureSet.difference_update(interface.getWorkerExposureSet()) + + return MemberCondition( + PropertyDefiner.getStringAttr(interfaceMember, + "Pref"), + PropertyDefiner.getStringAttr(interfaceMember, + "Func"), + getAvailableInTestFunc(interfaceMember), + descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name), + descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name), + nonExposureSet) def generatePrefableArray(self, array, name, specFormatter, specTerminator, specType, getCondition, getDataTuple, doIdArrays): @@ -2044,7 +2084,7 @@ class PropertyDefiner: specs = [] prefableSpecs = [] - prefableTemplate = ' { true, %s, %s, %s, %s, &%s[%d] }' + prefableTemplate = ' { true, %s, %s, %s, %s, %s, &%s[%d] }' prefCacheTemplate = '&%s[%d].enabled' def switchToCondition(props, condition): @@ -2056,7 +2096,8 @@ class PropertyDefiner: prefCacheTemplate % (name, len(prefableSpecs)))) # Set up pointers to the new sets of specs inside prefableSpecs prefableSpecs.append(prefableTemplate % - (condition.func, + (condition.nonExposedGlobals, + condition.func, condition.available, condition.checkAnyPermissions, condition.checkAllPermissions, @@ -2075,7 +2116,7 @@ class PropertyDefiner: # And the actual spec specs.append(specFormatter(getDataTuple(member))) specs.append(specTerminator) - prefableSpecs.append(" { false, nullptr }") + prefableSpecs.append(" { false, 0, nullptr, nullptr, nullptr, nullptr, nullptr }") specType = "const " + specType arrays = fill( @@ -2198,7 +2239,7 @@ class MethodDefiner(PropertyDefiner): "methodInfo": False, "length": 1, "flags": "0", - "condition": MemberCondition(None, condition) + "condition": MemberCondition(func=condition) }) continue @@ -2252,7 +2293,7 @@ class MethodDefiner(PropertyDefiner): "selfHostedName": "ArrayValues", "length": 0, "flags": "JSPROP_ENUMERATE", - "condition": MemberCondition(None, None) + "condition": MemberCondition() }) # Output an @@iterator for generated iterator interfaces. This should @@ -2268,7 +2309,7 @@ class MethodDefiner(PropertyDefiner): "selfHostedName": "IteratorIdentity", "length": 0, "flags": "0", - "condition": MemberCondition(None, None) + "condition": MemberCondition() }) # Generate the maplike/setlike iterator, if one wasn't already @@ -2341,7 +2382,7 @@ class MethodDefiner(PropertyDefiner): "length": 0, "flags": "JSPROP_ENUMERATE", # readonly/permanent added # automatically. - "condition": MemberCondition(None, None) + "condition": MemberCondition() }) if descriptor.interface.isJSImplemented(): @@ -2353,7 +2394,7 @@ class MethodDefiner(PropertyDefiner): "methodInfo": False, "length": 2, "flags": "0", - "condition": MemberCondition(None, None) + "condition": MemberCondition() }) else: for m in clearableCachedAttrs(descriptor): @@ -2364,7 +2405,7 @@ class MethodDefiner(PropertyDefiner): "methodInfo": False, "length": "0", "flags": "0", - "condition": MemberCondition(None, None) + "condition": MemberCondition() }) self.unforgeable = unforgeable diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index b27f02166ab1..261543f19293 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -53,6 +53,18 @@ struct ConstantSpec typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global); +namespace GlobalNames { +// The names of our possible globals. These are the names of the actual +// interfaces, not of the global names used to refer to them in IDL [Exposed] +// annotations. +static const uint32_t Window = 1u << 0; +static const uint32_t BackstagePass = 1u << 1; +static const uint32_t DedicatedWorkerGlobalScope = 1u << 2; +static const uint32_t SharedWorkerGlobalScope = 1u << 3; +static const uint32_t ServiceWorkerGlobalScope = 1u << 4; +static const uint32_t WorkerDebuggerGlobalScope = 1u << 5; +} // namespace GlobalNames + template struct Prefable { inline bool isEnabled(JSContext* cx, JS::Handle obj) const { @@ -85,6 +97,8 @@ struct Prefable { // A boolean indicating whether this set of specs is enabled bool enabled; + // Bitmask of global names that we should not be exposed in. + uint32_t nonExposedGlobals; // A function pointer to a function that can say the property is disabled // even if "enabled" is set to true. If the pointer is null the value of // "enabled" is used as-is unless availableFunc overrides. From e4fd35820fcb997585255b51938a74c54dc4121d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 1 Dec 2015 12:02:36 -0500 Subject: [PATCH 006/102] Bug 1048695 part 2. Make interface members not be exposed based on their nonExposedGlobals. r=peterv --- dom/bindings/BindingUtils.cpp | 49 +++++++++++++++++++++++++++++++++++ dom/bindings/BindingUtils.h | 10 ------- dom/bindings/DOMJSClass.h | 26 +++++++++++++++++++ dom/webidl/EventTarget.webidl | 2 +- 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 88150c2045d9..f08cc56d08bd 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2534,6 +2534,55 @@ CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissio return true; } +bool +IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal, + uint32_t aNonExposedGlobals) +{ + MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?"); + MOZ_ASSERT((aNonExposedGlobals & + ~(GlobalNames::Window | + GlobalNames::BackstagePass | + GlobalNames::DedicatedWorkerGlobalScope | + GlobalNames::SharedWorkerGlobalScope | + GlobalNames::ServiceWorkerGlobalScope | + GlobalNames::WorkerDebuggerGlobalScope)) == 0, + "Unknown non-exposed global type"); + + const char* name = js::GetObjectClass(aGlobal)->name; + + if ((aNonExposedGlobals & GlobalNames::Window) && + !strcmp(name, "Window")) { + return true; + } + + if ((aNonExposedGlobals & GlobalNames::BackstagePass) && + !strcmp(name, "BackstagePass")) { + return true; + } + + if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) && + !strcmp(name, "DedicatedWorkerGlobalScope")) { + return true; + } + + if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) && + !strcmp(name, "SharedWorkerGlobalScope")) { + return true; + } + + if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) && + !strcmp(name, "ServiceWorkerGlobalScope")) { + return true; + } + + if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) && + !strcmp(name, "WorkerDebuggerGlobalScopex")) { + return true; + } + + return false; +} + void HandlePrerenderingViolation(nsPIDOMWindow* aWindow) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 374386cbb8c6..1d7e64c355f7 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3160,16 +3160,6 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo, JS::Handle aValue); #endif -// Returns true if aObj's global has any of the permissions named in aPermissions -// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated. -bool -CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); - -// Returns true if aObj's global has all of the permissions named in aPermissions -// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated. -bool -CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); - // This function is called by the bindings layer for methods/getters/setters // that are not safe to be called in prerendering mode. It checks to make sure // that the |this| object is not running in a global that is in prerendering diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 261543f19293..8360e4507844 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -39,12 +39,24 @@ typedef bool JS::Handle obj, JS::AutoIdVector& props); +// Returns true if aObj's global has any of the permissions named in +// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be +// null-terminated. bool CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); +// Returns true if aObj's global has all of the permissions named in +// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be +// null-terminated. bool CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); +// Returns true if the given global is of a type whose bit is set in +// aNonExposedGlobals. +bool +IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal, + uint32_t aNonExposedGlobals); + struct ConstantSpec { const char* name; @@ -68,6 +80,20 @@ static const uint32_t WorkerDebuggerGlobalScope = 1u << 5; template struct Prefable { inline bool isEnabled(JSContext* cx, JS::Handle obj) const { + // Reading "enabled" on a worker thread is technically undefined behavior, + // because it's written only on main threads, with no barriers of any sort. + // So we want to avoid doing that. But we don't particularly want to make + // expensive NS_IsMainThread calls here. + // + // The good news is that "enabled" is only written for things that have a + // Pref annotation, and such things can never be exposed on non-Window + // globals; our IDL parser enforces that. So as long as we check our + // exposure set before checking "enabled" we will be ok. + if (nonExposedGlobals && + IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj), + nonExposedGlobals)) { + return false; + } if (!enabled) { return false; } diff --git a/dom/webidl/EventTarget.webidl b/dom/webidl/EventTarget.webidl index 6742fce2f0f3..be23ed1d4ac7 100644 --- a/dom/webidl/EventTarget.webidl +++ b/dom/webidl/EventTarget.webidl @@ -49,6 +49,6 @@ partial interface EventTarget { // chrome easier. This returns the window which can be used to create // events to fire at this EventTarget, or null if there isn't one. partial interface EventTarget { - [ChromeOnly, Exposed=Window, BinaryName="ownerGlobalForBindings"] + [ChromeOnly, Exposed=(Window,System), BinaryName="ownerGlobalForBindings"] readonly attribute WindowProxy? ownerGlobal; }; From d08b0cdd80d178177c3e6f1eda5dab9b67588826 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Tue, 1 Dec 2015 18:11:36 +0100 Subject: [PATCH 007/102] Bug 1226816: SharedStubs - Don't enable the call scripted get prop shared stub in ionmonkey yet, r=jandem --- js/src/jit-test/tests/ion/bug1226816.js | 11 +++++++++++ js/src/jit/SharedIC.cpp | 6 ++++-- js/src/jit/SharedIC.h | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug1226816.js diff --git a/js/src/jit-test/tests/ion/bug1226816.js b/js/src/jit-test/tests/ion/bug1226816.js new file mode 100644 index 000000000000..fc97c9c97d79 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1226816.js @@ -0,0 +1,11 @@ +// |jit-test| error: InternalError + +x = 1; +x; +function g(y) {} +g(this); +x = /x/; +function f() { + f(x.flags); +} +f(); diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 3c16c01b6eac..0219ce149575 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -2552,7 +2552,7 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod isTemporarilyUnoptimizable); // Try handling scripted getters. - if (cacheableCall && isScripted && !isDOMProxy) { + if (cacheableCall && isScripted && !isDOMProxy && engine == ICStubCompiler::Engine::Baseline) { RootedFunction callee(cx, &shape->getterObject()->as()); MOZ_ASSERT(callee->hasScript()); @@ -2565,7 +2565,7 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod JitSpew(JitSpew_BaselineIC, " Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub", callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno()); - ICGetProp_CallScripted::Compiler compiler(cx, engine, monitorStub, obj, holder, callee, + ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee, script->pcToOffset(pc)); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) @@ -3507,6 +3507,8 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failureLeaveStubFrame; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 4112a3b93e2b..56dcfcf9515f 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -2931,9 +2931,9 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, HandleObject obj, + Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, HandleFunction getter, uint32_t pcOffset) - : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, engine, + : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, Engine::Baseline, firstMonitorStub, obj, holder, getter, pcOffset, /* outerClass = */ nullptr) {} From c0cc327f56ce31e588e40f4e4b39143d37bf46e9 Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Tue, 1 Dec 2015 10:13:58 -0800 Subject: [PATCH 008/102] Bug 1134253 - Improve object extensibility TypeError messages produced from Object.defineProperty; r=jorendorff --HG-- extra : rebase_source : a1078b7e6b5605054553f5b445b21f989881f99c --- js/src/js.msg | 3 ++- js/src/vm/NativeObject.cpp | 2 +- js/src/vm/Xdr.h | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index 4385d0223805..a0543737b18d 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -86,7 +86,8 @@ MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} w MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 1, JSEXN_TYPEERR, "{0} is not a non-null object") MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 1, JSEXN_TYPEERR, "can't assign to properties of {0}: not an object") MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified") -MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0} is not extensible") +MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0}: Object is not extensible") +MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE, 2, JSEXN_TYPEERR, "can't define property {1}: {0} is not extensible") MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}") MSG_DEF(JSMSG_CANT_APPEND_TO_ARRAY, 0, JSEXN_TYPEERR, "can't add elements past the end of an array if its length property is unwritable") MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length") diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index afc8ceff5bdb..1ae67548bf2d 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1360,7 +1360,7 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId // Step 2. if (!shape) { if (!obj->nonProxyIsExtensible()) - return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); + return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE); // Fill in missing desc fields with defaults. CompletePropertyDescriptor(&desc); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 2502bd36e2cb..39aad3900193 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 323; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 324; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 421, +static_assert(JSErr_Limit == 422, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " From 20bcd25937e0577c62b9bd67d3c7e530f6e8b4c7 Mon Sep 17 00:00:00 2001 From: Kate McKinley Date: Tue, 24 Nov 2015 10:40:02 +0900 Subject: [PATCH 009/102] Bug 1191460 Rebased patch and added userContextId to origin attributes. (r=tanvi,r=sicking) --- docshell/base/nsDocShell.cpp | 9 ++++++ docshell/base/nsDocShell.h | 4 +++ dom/base/nsFrameLoader.cpp | 54 +++++++++++++++++++++++++++++------- dom/base/nsGkAtomList.h | 3 ++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 678ad864fe58..be0cf13421d0 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13745,6 +13745,13 @@ nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg) return NS_OK; } +NS_IMETHODIMP +nsDocShell::SetUserContextId(uint32_t aUserContextId) +{ + mUserContextId = aUserContextId; + return NS_OK; +} + /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsBrowserElement(bool* aIsBrowser) { @@ -13854,6 +13861,8 @@ nsDocShell::GetOriginAttributes() attrs.mAppId = mOwnOrContainingAppId; } + attrs.mUserContextId = mUserContextId; + if (mFrameType == eFrameTypeBrowser) { attrs.mInBrowser = true; } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index f9f69090553d..d76243f5d8ad 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -232,6 +232,7 @@ public: NS_IMETHOD GetUseRemoteTabs(bool*) override; NS_IMETHOD SetRemoteTabs(bool) override; NS_IMETHOD GetOriginAttributes(JS::MutableHandle) override; + NS_IMETHOD SetUserContextId(uint32_t); // Restores a cached presentation from history (mLSHE). // This method swaps out the content viewer and simulates loads for @@ -1002,6 +1003,9 @@ protected: // find it by walking up the docshell hierarchy.) uint32_t mOwnOrContainingAppId; + // userContextId signifying which container we are in + uint32_t mUserContextId; + nsString mPaymentRequestId; nsString GetInheritedPaymentRequestId(); diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 6fd51f2fd956..2bf7f411f833 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -236,7 +236,7 @@ nsFrameLoader::LoadFrame() if (NS_SUCCEEDED(rv)) { rv = LoadURI(uri); } - + if (NS_FAILED(rv)) { FireErrorEvent(); @@ -319,7 +319,7 @@ nsFrameLoader::ReallyStartLoading() if (NS_FAILED(rv)) { FireErrorEvent(); } - + return rv; } @@ -339,7 +339,7 @@ nsFrameLoader::ReallyStartLoadingInternal() // FIXME get error codes from child mRemoteBrowser->LoadURL(mURIToLoad); - + if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) { NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n"); } @@ -372,7 +372,7 @@ nsFrameLoader::ReallyStartLoadingInternal() loadInfo->SetOwner(mOwnerContent->NodePrincipal()); nsCOMPtr referrer; - + nsAutoString srcdoc; bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, @@ -546,7 +546,7 @@ nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, { NS_PRECONDITION(aItem, "Must have docshell treeitem"); NS_PRECONDITION(mOwnerContent, "Must have owning content"); - + nsAutoString value; bool isContent = false; mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value); @@ -1127,7 +1127,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, !AllDescendantsOfType(otherDocshell, otherType))) { return NS_ERROR_NOT_IMPLEMENTED; } - + // Save off the tree owners, frame elements, chrome event handlers, and // docshell and document parents before doing anything else. nsCOMPtr ourOwner, otherOwner; @@ -1237,7 +1237,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, ourOwner->ContentShellRemoved(ourDocshell); otherOwner->ContentShellRemoved(otherDocshell); } - + ourParentItem->AddChild(otherDocshell); otherParentItem->AddChild(ourDocshell); @@ -1788,6 +1788,23 @@ nsFrameLoader::MaybeCreateDocShell() mDocShell->SetName(frameName); } + //Grab the userContextId from owner if XUL + nsAutoString userContextIdStr; + if (namespaceID == kNameSpaceID_XUL) { + if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) { + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::usercontextid, + userContextIdStr); + } + } + + if (!userContextIdStr.IsEmpty()) { + nsresult err; + nsDocShell * ds = nsDocShell::Cast(mDocShell); + ds->SetUserContextId(userContextIdStr.ToInteger(&err)); + NS_ENSURE_SUCCESS(err, err); + } + // Inform our docShell that it has a new child. // Note: This logic duplicates a lot of logic in // nsSubDocumentFrame::AttributeChanged. We should fix that. @@ -1961,7 +1978,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) NS_WARN_IF_FALSE(treeOwner, "Trying to load a new url to a docshell without owner!"); NS_ENSURE_STATE(treeOwner); - + if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) { // No need to do recursion-protection here XXXbz why not?? Do we really // trust people not to screw up with non-content docshells? @@ -1975,7 +1992,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) int32_t depth = 0; while (parentAsItem) { ++depth; - + if (depth >= MAX_DEPTH_CONTENT_FRAMES) { mDepthTooGreat = true; NS_WARNING("Too many nested content frames so giving up"); @@ -2016,7 +2033,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) bool equal; rv = aURI->EqualsExceptRef(parentURI, &equal); NS_ENSURE_SUCCESS(rv, rv); - + if (equal) { matchCount++; if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) { @@ -3064,6 +3081,23 @@ nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, // Populate packageId to signedPkg. attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId); + // set the userContextId on the attrs before we pass them into + // the tab context + if (mOwnerContent) { + nsAutoString userContextIdStr; + if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) { + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::usercontextid, + userContextIdStr); + } + if (!userContextIdStr.IsEmpty()) { + nsresult err; + uint32_t userContextId = userContextIdStr.ToInteger(&err); + NS_ENSURE_SUCCESS(err, err); + attrs.mUserContextId = userContextId; + } + } + bool tabContextUpdated = aTabContext->SetTabContext(ownApp, containingApp, attrs, signedPkgOrigin); NS_ENSURE_STATE(tabContextUpdated); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 2ea18d2dbc2a..c63a97f6416e 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2405,3 +2405,6 @@ GK_ATOM(onboundary, "onboundary") #endif GK_ATOM(vr_state, "vr-state") + +// Contextual Identity / Containers +GK_ATOM(usercontextid, "usercontextid") From c4b3854e9c69f0c514e63f171f2d17be987c3f9e Mon Sep 17 00:00:00 2001 From: Kate McKinley Date: Tue, 1 Dec 2015 10:21:42 -0800 Subject: [PATCH 010/102] Bug 1191460 Contextual Identity tests r=tanvi,r=ttaubert --- .../components/contextualidentity/moz.build | 12 +++ .../test/browser/browser.ini | 7 ++ .../test/browser/browser_usercontext.js | 91 +++++++++++++++++++ .../file_reflect_cookie_into_title.html | 23 +++++ browser/components/moz.build | 1 + 5 files changed, 134 insertions(+) create mode 100644 browser/components/contextualidentity/moz.build create mode 100644 browser/components/contextualidentity/test/browser/browser.ini create mode 100644 browser/components/contextualidentity/test/browser/browser_usercontext.js create mode 100644 browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html diff --git a/browser/components/contextualidentity/moz.build b/browser/components/contextualidentity/moz.build new file mode 100644 index 000000000000..7e3ff07cbd79 --- /dev/null +++ b/browser/components/contextualidentity/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +BROWSER_CHROME_MANIFESTS += [ + 'test/browser/browser.ini', +] + +with Files('**'): + BUG_COMPONENT = ('Firefox', 'Contextual Identity') diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini new file mode 100644 index 000000000000..58611df0ad03 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/browser.ini @@ -0,0 +1,7 @@ +[DEFAULT] +skip-if = buildapp == "mulet" +support-files = + file_reflect_cookie_into_title.html + +[browser_usercontext.js] +skip-if = e10s diff --git a/browser/components/contextualidentity/test/browser/browser_usercontext.js b/browser/components/contextualidentity/test/browser/browser_usercontext.js new file mode 100644 index 000000000000..b59b5ac4c361 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + +const USER_CONTEXTS = [ + "default", + "personal", + "work", +]; + +const BASE_URI = "http://mochi.test:8888/browser/browser/components/" + + "contextualidentity/test/browser/file_reflect_cookie_into_title.html"; + + +// opens `uri' in a new tab with the provided userContextId and focuses it. +// returns the newly opened tab +function openTabInUserContext(uri, userContextId) { + // open the tab in the correct userContextId + let tab = gBrowser.addTab(uri, {userContextId}); + + // select tab and make sure its browser is focused + gBrowser.selectedTab = tab; + tab.ownerDocument.defaultView.focus(); + + return tab; +} + +add_task(function* setup() { + // make sure userContext is enabled. + SpecialPowers.pushPrefEnv({"set": [ + ["privacy.userContext.enabled", true] + ]}); +}); + +add_task(function* cleanup() { + // make sure we don't leave any prefs set for the next tests + registerCleanupFunction(function() { + SpecialPowers.popPrefEnv(); + }); +}); + +add_task(function* test() { + for (let userContextId of Object.keys(USER_CONTEXTS)) { + // load the page in 3 different contexts and set a cookie + // which should only be visible in that context + let cookie = USER_CONTEXTS[userContextId]; + + // open our tab in the given user context + let tab = openTabInUserContext(BASE_URI+"?"+cookie, userContextId); + + // wait for tab load + yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab)); + + // remove the tab + gBrowser.removeTab(tab); + } + + { + // Set a cookie in a different context so we can detect if that affects + // cross-context properly. If we don't do that, we get an UNEXPECTED-PASS + // for the localStorage case for the last tab we set. + let tab = openTabInUserContext(BASE_URI+"?foo", 9999); + yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab)); + gBrowser.removeTab(tab); + } + + for (let userContextId of Object.keys(USER_CONTEXTS)) { + // Load the page without setting the cookie this time + let expectedContext = USER_CONTEXTS[userContextId]; + + let tab = openTabInUserContext(BASE_URI, userContextId); + + // wait for load + let browser = gBrowser.getBrowserForTab(tab); + yield BrowserTestUtils.browserLoaded(browser); + + // get the title + let title = browser.contentDocument.title.trim().split("|"); + + // check each item in the title and validate it meets expectatations + for (let part of title) { + let [storageMethodName, value] = part.split("="); + let is_f = storageMethodName == "cookie" ? is : todo_is; + is_f(value, expectedContext, + "the title reflects the expected contextual identity of " + + expectedContext + " for method " + storageMethodName + ": " + value); + } + + gBrowser.removeTab(tab); + } +}); diff --git a/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html new file mode 100644 index 000000000000..b04f3fd5ca81 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html @@ -0,0 +1,23 @@ + + + + title not set + + + + + diff --git a/browser/components/moz.build b/browser/components/moz.build index 74461650a938..25a45812e033 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -6,6 +6,7 @@ DIRS += [ 'about', + 'contextualidentity', 'customizableui', 'dirprovider', 'downloads', From 13179602dc5da6777be90b7234ad7d2e6cdf6403 Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Tue, 1 Dec 2015 11:53:14 -0700 Subject: [PATCH 011/102] Bug 1222933: Backout previous DLL blocklist change as it had no effect on crash; r=backout --- mozglue/build/WindowsDllBlocklist.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp index b5903f36ba57..319edb99bfe1 100644 --- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/build/WindowsDllBlocklist.cpp @@ -183,9 +183,6 @@ static DllBlockInfo sWindowsDllBlocklist[] = { // NetOp School, discontinued product, bug 763395 { "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) }, - // AdMuncher, bug 1222933 - { "am32-34121.dll", MAKE_VERSION(4, 73, 0, 30530) }, - { nullptr, 0 } }; From 4a4f8a6547ab3acebab008453168b4abe2d6dc67 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 16 Nov 2015 21:56:36 -0800 Subject: [PATCH 012/102] Bug 1225298 - Use GCHashSet for atoms table, r=terrence --HG-- extra : rebase_source : e091995dec92f0bd17cfe06885ffc2037cfdcb9d --- js/src/jsatom.cpp | 16 ++-------------- js/src/jsatom.h | 9 ++++++++- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 1341a24a38cf..3d3c0e4d821e 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -245,20 +245,8 @@ js::MarkWellKnownSymbols(JSTracer* trc) void JSRuntime::sweepAtoms() { - if (!atoms_) - return; - - for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) { - AtomStateEntry entry = e.front(); - JSAtom* atom = entry.asPtrUnbarriered(); - bool isDying = IsAboutToBeFinalizedUnbarriered(&atom); - - /* Pinned or interned key cannot be finalized. */ - MOZ_ASSERT_IF(hasContexts() && entry.isPinned(), !isDying); - - if (isDying) - e.removeFront(); - } + if (atoms_) + atoms_->sweep(); } bool diff --git a/js/src/jsatom.h b/js/src/jsatom.h index da57455bd863..ed1a6c9adf85 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -12,8 +12,10 @@ #include "jsalloc.h" #include "gc/Barrier.h" +#include "gc/Marking.h" #include "gc/Rooting.h" #include "js/GCAPI.h" +#include "js/GCHashTable.h" #include "vm/CommonPropertyNames.h" class JSAtom; @@ -76,6 +78,11 @@ class AtomStateEntry JSAtom* asPtr() const; JSAtom* asPtrUnbarriered() const; + + bool needsSweep() { + JSAtom* atom = asPtrUnbarriered(); + return gc::IsAboutToBeFinalizedUnbarriered(&atom); + } }; struct AtomHasher @@ -111,7 +118,7 @@ struct AtomHasher static void rekey(AtomStateEntry& k, const AtomStateEntry& newKey) { k = newKey; } }; -typedef HashSet AtomSet; +using AtomSet = js::GCHashSet; // This class is a wrapper for AtomSet that is used to ensure the AtomSet is // not modified. It should only expose read-only methods from AtomSet. From 642ecb4eeca3bcbc3eeeabcfb82fba7590419331 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 19 Nov 2015 11:20:44 -0800 Subject: [PATCH 013/102] Bug 1225298 - Use GCHashSet for SavedStack set of frames, r=terrence --HG-- extra : rebase_source : 3d713bc3f020a112b6026a0f9605ba7b8427466b --- js/src/vm/SavedFrame.h | 7 ++++--- js/src/vm/SavedStacks.cpp | 8 +------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/js/src/vm/SavedFrame.h b/js/src/vm/SavedFrame.h index 74c2726b3c28..484ba0feac34 100644 --- a/js/src/vm/SavedFrame.h +++ b/js/src/vm/SavedFrame.h @@ -9,6 +9,7 @@ #include "jswrapper.h" +#include "js/GCHashTable.h" #include "js/UbiNode.h" namespace js { @@ -133,9 +134,9 @@ class SavedFrame : public NativeObject { struct Lookup; struct HashPolicy; - typedef HashSet, - HashPolicy, - SystemAllocPolicy> Set; + typedef GCHashSet, + HashPolicy, + SystemAllocPolicy> Set; class AutoLookupVector; diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 5a053efd3d22..45e04ca10a40 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1035,13 +1035,7 @@ SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString void SavedStacks::sweep(JSRuntime* rt) { - if (frames.initialized()) { - for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) { - if (IsAboutToBeFinalized(&e.mutableFront())) - e.removeFront(); - } - } - + frames.sweep(); sweepPCLocationMap(); } From 96199a6125ed8b2dfc11679768ed8f76d4caa71b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 18 Nov 2015 08:58:57 -0800 Subject: [PATCH 014/102] Bug 1225298 - Use GCHashSet for SymbolRegistry, r=terrence --HG-- extra : rebase_source : a26b02eee734cb4dbacd4329c96d9e56683b2446 --- js/src/vm/Symbol.cpp | 12 ------------ js/src/vm/Symbol.h | 16 +++++----------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/js/src/vm/Symbol.cpp b/js/src/vm/Symbol.cpp index 66081123d099..dfe6278d1400 100644 --- a/js/src/vm/Symbol.cpp +++ b/js/src/vm/Symbol.cpp @@ -105,18 +105,6 @@ Symbol::dump(FILE* fp) } #endif // DEBUG -void -SymbolRegistry::sweep() -{ - for (Enum e(*this); !e.empty(); e.popFront()) { - mozilla::DebugOnly sym = e.front().unbarrieredGet(); - if (IsAboutToBeFinalized(&e.mutableFront())) - e.removeFront(); - else - MOZ_ASSERT(sym == e.front().unbarrieredGet()); - } -} - bool js::SymbolDescriptiveString(JSContext* cx, Symbol* sym, MutableHandleValue result) { diff --git a/js/src/vm/Symbol.h b/js/src/vm/Symbol.h index 977f17d4be85..c201ed127c50 100644 --- a/js/src/vm/Symbol.h +++ b/js/src/vm/Symbol.h @@ -17,6 +17,7 @@ #include "gc/Barrier.h" #include "gc/Marking.h" +#include "js/GCHashTable.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" @@ -94,14 +95,6 @@ struct HashSymbolsByDescription } }; -/* - * Hash table that implements the symbol registry. - * - * This must be a typedef for the benefit of GCC 4.4.6 (used to build B2G for Ice - * Cream Sandwich). - */ -typedef HashSet SymbolHashSet; - /* * The runtime-wide symbol registry, used to implement Symbol.for(). * @@ -117,11 +110,12 @@ typedef HashSet { public: - SymbolRegistry() : SymbolHashSet() {} - void sweep(); + SymbolRegistry() {} }; } /* namespace js */ From 244cf5d69d218e47ced2ad6b078b6fd4e6ed7e27 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 19 Nov 2015 11:20:53 -0800 Subject: [PATCH 015/102] Bug 1225298 - Use GCHashSet for InnerViewTable, r=terrence --HG-- extra : rebase_source : 715f2bb5a591efd1acdb28ba1c83f5f2c9c06855 --- js/src/jscompartment.cpp | 4 ++-- js/src/vm/ArrayBufferObject.cpp | 17 +++++------------ js/src/vm/ArrayBufferObject.h | 22 +++++++++++++++------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 66433ab1dc5d..19f8aba465f7 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -667,13 +667,13 @@ JSCompartment::sweepAfterMinorGC() globalWriteBarriered = false; if (innerViews.needsSweepAfterMinorGC()) - innerViews.sweepAfterMinorGC(runtimeFromMainThread()); + innerViews.sweepAfterMinorGC(); } void JSCompartment::sweepInnerViews() { - innerViews.sweep(runtimeFromAnyThread()); + innerViews.sweep(); } void diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index d68ddae867a9..8814f8a9a742 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -1114,7 +1114,7 @@ InnerViewTable::removeViews(ArrayBufferObject* buffer) map.remove(p); } -bool +/* static */ bool InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views) { if (IsAboutToBeFinalizedUnbarriered(pkey)) @@ -1132,21 +1132,14 @@ InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views) } void -InnerViewTable::sweep(JSRuntime* rt) +InnerViewTable::sweep() { MOZ_ASSERT(nurseryKeys.empty()); - - if (!map.initialized()) - return; - - for (Map::Enum e(map); !e.empty(); e.popFront()) { - if (sweepEntry(&e.front().mutableKey(), e.front().value())) - e.removeFront(); - } + map.sweep(); } void -InnerViewTable::sweepAfterMinorGC(JSRuntime* rt) +InnerViewTable::sweepAfterMinorGC() { MOZ_ASSERT(needsSweepAfterMinorGC()); @@ -1164,7 +1157,7 @@ InnerViewTable::sweepAfterMinorGC(JSRuntime* rt) } else { // Do the required sweeping by looking at every map entry. nurseryKeys.clear(); - sweep(rt); + sweep(); nurseryKeysValid = true; } diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 998ed35bda3a..6cd81d325f2c 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -10,6 +10,7 @@ #include "jsobj.h" #include "builtin/TypedObjectConstants.h" +#include "js/GCHashTable.h" #include "vm/Runtime.h" #include "vm/SharedMem.h" @@ -501,6 +502,12 @@ class InnerViewTable friend class ArrayBufferObject; private: + struct MapGCPolicy { + static bool needsSweep(JSObject** key, ViewVector* value) { + return InnerViewTable::sweepEntry(key, *value); + } + }; + // This key is a raw pointer and not a ReadBarriered because the post- // barrier would hold nursery-allocated entries live unconditionally. It is // a very common pattern in low-level and performance-oriented JavaScript @@ -509,10 +516,11 @@ class InnerViewTable // performance regression. Thus, it is vital that nursery pointers in this // map not be held live. Special support is required in the minor GC, // implemented in sweepAfterMinorGC. - typedef HashMap, - SystemAllocPolicy> Map; + typedef GCHashMap, + SystemAllocPolicy, + MapGCPolicy> Map; // For all objects sharing their storage with some other view, this maps // the object to the list of such views. All entries in this map are weak. @@ -529,7 +537,7 @@ class InnerViewTable bool nurseryKeysValid; // Sweep an entry during GC, returning whether the entry should be removed. - bool sweepEntry(JSObject** pkey, ViewVector& views); + static bool sweepEntry(JSObject** pkey, ViewVector& views); bool addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewObject* view); ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj); @@ -542,8 +550,8 @@ class InnerViewTable // Remove references to dead objects in the table and update table entries // to reflect moved objects. - void sweep(JSRuntime* rt); - void sweepAfterMinorGC(JSRuntime* rt); + void sweep(); + void sweepAfterMinorGC(); bool needsSweepAfterMinorGC() { return !nurseryKeys.empty() || !nurseryKeysValid; From fd192a968a14087382d4364d4cdf1ec6f90295bb Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 19 Nov 2015 11:21:07 -0800 Subject: [PATCH 016/102] Bug 1225298 - Use GCHashMap for liveScopes, r=terrence --HG-- extra : rebase_source : b364399010d0eadaade561de310125ec3f1f8ed1 --- js/src/vm/ScopeObject.cpp | 20 ++++++++------------ js/src/vm/ScopeObject.h | 12 +++++++----- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index f4a37015d688..5aee1ae73ba2 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1547,11 +1547,12 @@ MissingScopeKey::match(MissingScopeKey sk1, MissingScopeKey sk2) return sk1.frame_ == sk2.frame_ && sk1.staticScope_ == sk2.staticScope_; } -void -LiveScopeVal::sweep() +bool +LiveScopeVal::needsSweep() { if (staticScope_) MOZ_ALWAYS_FALSE(IsAboutToBeFinalized(&staticScope_)); + return false; } // Live ScopeIter values may be added to DebugScopes::liveScopes, as @@ -2402,16 +2403,11 @@ DebugScopes::sweep(JSRuntime* rt) } } - for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { - e.front().value().sweep(); - - /* - * Scopes can be finalized when a debugger-synthesized ScopeObject is - * no longer reachable via its DebugScopeObject. - */ - if (IsAboutToBeFinalized(&e.front().mutableKey())) - e.removeFront(); - } + /* + * Scopes can be finalized when a debugger-synthesized ScopeObject is + * no longer reachable via its DebugScopeObject. + */ + liveScopes.sweep(); } #ifdef JSGC_HASH_TABLE_CHECKS diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 4e09f235184f..efe72bea3400 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -13,6 +13,7 @@ #include "builtin/ModuleObject.h" #include "gc/Barrier.h" +#include "js/GCHashTable.h" #include "vm/ArgumentsObject.h" #include "vm/ProxyObject.h" @@ -1101,7 +1102,6 @@ class LiveScopeVal AbstractFramePtr frame_; RelocatablePtrObject staticScope_; - void sweep(); static void staticAsserts(); public: @@ -1114,6 +1114,8 @@ class LiveScopeVal JSObject* staticScope() const { return staticScope_; } void updateFrame(AbstractFramePtr frame) { frame_ = frame; } + + bool needsSweep(); }; /*****************************************************************************/ @@ -1216,10 +1218,10 @@ class DebugScopes * removes scopes as they are popped). Thus, two consecutive debugger lazy * updates of liveScopes need only fill in the new scopes. */ - typedef HashMap, - LiveScopeVal, - MovableCellHasher>, - RuntimeAllocPolicy> LiveScopeMap; + typedef GCHashMap, + LiveScopeVal, + MovableCellHasher>, + RuntimeAllocPolicy> LiveScopeMap; LiveScopeMap liveScopes; static MOZ_ALWAYS_INLINE void liveScopesPostWriteBarrier(JSRuntime* rt, LiveScopeMap* map, ScopeObject* key); From ff55707b8392cb2b86f9a7fbd313b99553d02d65 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 18 Nov 2015 17:37:03 -0800 Subject: [PATCH 017/102] Bug 1225298 - Use GCHashMap for UniqueIdMap, r=terrence --HG-- extra : rebase_source : 322f5b64eb3e3e37d3ab51ca813b05fe8e78e7bd --- js/public/TracingAPI.h | 1 + js/src/gc/Zone.h | 11 ++++++++++- js/src/jsgc.cpp | 14 +++++++------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index d6f760e09244..08ced6818517 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -413,6 +413,7 @@ struct DefaultGCPolicy }; template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; +template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; template struct DefaultGCPolicy> diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ea3c14df6f15..5519823ada2d 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -16,6 +16,7 @@ #include "ds/SplayTree.h" #include "gc/FindSCCs.h" #include "gc/GCRuntime.h" +#include "js/GCHashTable.h" #include "js/TracingAPI.h" #include "vm/MallocProvider.h" #include "vm/TypeInference.h" @@ -62,8 +63,16 @@ class ZoneHeapThreshold const AutoLockGC& lock); }; +struct UniqueIdGCPolicy { + static bool needsSweep(Cell** cell, uint64_t* value); +}; + // Maps a Cell* to a unique, 64bit id. -using UniqueIdMap = HashMap, SystemAllocPolicy>; +using UniqueIdMap = GCHashMap, + SystemAllocPolicy, + UniqueIdGCPolicy>; extern uint64_t NextCellUniqueId(JSRuntime* rt); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index dfb414413003..610fd78b0b6c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3662,16 +3662,16 @@ struct IsAboutToBeFinalizedFunctor { } }; +/* static */ bool +UniqueIdGCPolicy::needsSweep(Cell** cell, uint64_t*) +{ + return DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), (*cell)->getTraceKind(), cell); +} + void JS::Zone::sweepUniqueIds(js::FreeOp* fop) { - for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) { - if (DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), e.front().key()->getTraceKind(), - &e.front().mutableKey())) - { - e.removeFront(); - } - } + uniqueIds_.sweep(); } /* From cc8c87cbc0c9becc78a9ea28044fe544d6fa279a Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 1 Dec 2015 19:22:20 +0200 Subject: [PATCH 018/102] Bug 1134648, handle dynamic changes to rel=dns-prefetch, r=bz --HG-- extra : rebase_source : 817f331da1cfadb0833a14a3a18c5d4769752b79 --- dom/base/Link.cpp | 26 +++++++++ dom/base/Link.h | 5 ++ dom/html/HTMLAnchorElement.cpp | 34 +++++++----- dom/html/HTMLLinkElement.cpp | 76 ++++++++++++++++++++++++++ dom/html/HTMLLinkElement.h | 10 ++++ dom/xml/nsXMLContentSink.cpp | 7 --- parser/html/nsHtml5DocumentBuilder.cpp | 7 --- 7 files changed, 136 insertions(+), 29 deletions(-) diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp index d8d76dfab158..cacb884bbab9 100644 --- a/dom/base/Link.cpp +++ b/dom/base/Link.cpp @@ -14,6 +14,7 @@ #include "nsEscape.h" #include "nsGkAtoms.h" +#include "nsHTMLDNSPrefetch.h" #include "nsString.h" #include "mozAutoDocUpdate.h" @@ -46,6 +47,31 @@ Link::ElementHasHref() const mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href))); } +void +Link::TryDNSPrefetch() +{ + MOZ_ASSERT(mElement->IsInComposedDoc()); + if (ElementHasHref() && nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) { + nsHTMLDNSPrefetch::PrefetchLow(this); + } +} + +void +Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag, + nsWrapperCache::FlagsType aRequestedFlag) +{ + // If prefetch was deferred, clear flag and move on + if (mElement->HasFlag(aDeferredFlag)) { + mElement->UnsetFlags(aDeferredFlag); + // Else if prefetch was requested, clear flag and send cancellation + } else if (mElement->HasFlag(aRequestedFlag)) { + mElement->UnsetFlags(aRequestedFlag); + // Possible that hostname could have changed since binding, but since this + // covers common cases, most DNS prefetch requests will be canceled + nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); + } +} + void Link::SetLinkState(nsLinkState aState) { diff --git a/dom/base/Link.h b/dom/base/Link.h index 60c594e3ee1c..38b9792b7e06 100644 --- a/dom/base/Link.h +++ b/dom/base/Link.h @@ -111,6 +111,11 @@ public: bool ElementHasHref() const; + void TryDNSPrefetch(); + + void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag, + nsWrapperCache::FlagsType aRequestedFlag); + protected: virtual ~Link(); diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index 91bbe3ad2a42..f9dea267bcc1 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -158,9 +158,7 @@ HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIDocument* doc = GetComposedDoc(); if (doc) { doc->RegisterPendingLinkUpdate(this); - if (nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) { - nsHTMLDNSPrefetch::PrefetchLow(this); - } + TryDNSPrefetch(); } return rv; @@ -172,18 +170,9 @@ HTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) // Cancel any DNS prefetches // Note: Must come before ResetLinkState. If called after, it will recreate // mCachedURI based on data that is invalid - due to a call to GetHostname. + CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, + HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - // If prefetch was deferred, clear flag and move on - if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED)) - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); - // Else if prefetch was requested, clear flag and send cancellation - else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - // Possible that hostname could have changed since binding, but since this - // covers common cases, most DNS prefetch requests will be canceled - nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); - } - // If this link is ever reinserted into a document, it might // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false, Link::ElementHasHref()); @@ -406,6 +395,10 @@ HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, reset = true; } } + if (reset) { + CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, + HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + } } nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, @@ -418,6 +411,9 @@ HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // to get updated information about the visitedness from Link. if (reset) { Link::ResetLinkState(!!aNotify, true); + if (IsInComposedDoc()) { + TryDNSPrefetch(); + } } return rv; @@ -427,6 +423,14 @@ nsresult HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, bool aNotify) { + bool href = + (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID); + + if (href) { + CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, + HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + } + nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); @@ -435,7 +439,7 @@ HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, // we will need the updated attribute value because notifying the document // that content states have changed will call IntrinsicState, which will try // to get updated information about the visitedness from Link. - if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { + if (href) { Link::ResetLinkState(!!aNotify, false); } diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index ddaab02560be..1e13b8d859b0 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -28,6 +28,22 @@ #include "nsStyleConsts.h" #include "nsUnicharUtils.h" +#define LINK_ELEMENT_FLAG_BIT(n_) \ + NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_)) + +// Link element specific bits +enum { + // Indicates that a DNS Prefetch has been requested from this Link element. + HTML_LINK_DNS_PREFETCH_REQUESTED = LINK_ELEMENT_FLAG_BIT(0), + + // Indicates that a DNS Prefetch was added to the deferral queue + HTML_LINK_DNS_PREFETCH_DEFERRED = LINK_ELEMENT_FLAG_BIT(1) +}; + +#undef LINK_ELEMENT_FLAG_BIT + +ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2); + NS_IMPL_NS_NEW_HTML_ELEMENT(Link) namespace mozilla { @@ -126,6 +142,26 @@ HTMLLinkElement::SetItemValueText(const nsAString& aValue) SetHref(aValue); } +void +HTMLLinkElement::OnDNSPrefetchRequested() +{ + UnsetFlags(HTML_LINK_DNS_PREFETCH_DEFERRED); + SetFlags(HTML_LINK_DNS_PREFETCH_REQUESTED); +} + +void +HTMLLinkElement::OnDNSPrefetchDeferred() +{ + UnsetFlags(HTML_LINK_DNS_PREFETCH_REQUESTED); + SetFlags(HTML_LINK_DNS_PREFETCH_DEFERRED); +} + +bool +HTMLLinkElement::HasDeferredDNSPrefetchRequest() +{ + return HasFlag(HTML_LINK_DNS_PREFETCH_DEFERRED); +} + nsresult HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -145,6 +181,9 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, if (IsInComposedDoc()) { UpdatePreconnect(); + if (HasDNSPrefetchRel()) { + TryDNSPrefetch(); + } } void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal; @@ -173,6 +212,12 @@ HTMLLinkElement::LinkRemoved() void HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent) { + // Cancel any DNS prefetches + // Note: Must come before ResetLinkState. If called after, it will recreate + // mCachedURI based on data that is invalid - due to a call to GetHostname. + CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED, + HTML_LINK_DNS_PREFETCH_REQUESTED); + // If this link is ever reinserted into a document, it might // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false, Link::ElementHasHref()); @@ -322,6 +367,32 @@ HTMLLinkElement::UpdatePreconnect() } } +bool +HTMLLinkElement::HasDNSPrefetchRel() +{ + nsAutoString rel; + if (GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) { + return !!(ParseLinkTypes(rel, NodePrincipal()) & + nsStyleLinkElement::eDNS_PREFETCH); + } + + return false; +} + +nsresult +HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None && + (aName == nsGkAtoms::href || aName == nsGkAtoms::rel)) { + CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED, + HTML_LINK_DNS_PREFETCH_REQUESTED); + } + + return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, + aValue, aNotify); +} + nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify) @@ -368,6 +439,11 @@ HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } + if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) && + HasDNSPrefetchRel() && IsInComposedDoc()) { + TryDNSPrefetch(); + } + UpdateStyleSheetInternal(nullptr, nullptr, dropSheet || (aName == nsGkAtoms::title || diff --git a/dom/html/HTMLLinkElement.h b/dom/html/HTMLLinkElement.h index 12c295a722a8..0411bcede7ae 100644 --- a/dom/html/HTMLLinkElement.h +++ b/dom/html/HTMLLinkElement.h @@ -61,6 +61,9 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; + virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, + bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify) override; @@ -77,6 +80,10 @@ public: void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName); + virtual void OnDNSPrefetchDeferred() override; + virtual void OnDNSPrefetchRequested() override; + virtual bool HasDeferredDNSPrefetchRequest() override; + // WebIDL bool Disabled(); void SetDisabled(bool aDisabled); @@ -166,6 +173,9 @@ protected: // nsGenericHTMLElement virtual void GetItemValueText(DOMString& text) override; virtual void SetItemValueText(const nsAString& text) override; + + bool HasDNSPrefetchRel(); + RefPtr mRelList; private: RefPtr mImportLoader; diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 536794ca65e9..ba209234ee79 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -611,13 +611,6 @@ nsXMLContentSink::CloseElement(nsIContent* aContent) PrefetchHref(hrefVal, aContent, hasPrefetch); } } - if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { - nsAutoString hrefVal; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); - if (!hrefVal.IsEmpty()) { - PrefetchDNS(hrefVal); - } - } } } } diff --git a/parser/html/nsHtml5DocumentBuilder.cpp b/parser/html/nsHtml5DocumentBuilder.cpp index 60d5ba729f93..ea5fd4413477 100644 --- a/parser/html/nsHtml5DocumentBuilder.cpp +++ b/parser/html/nsHtml5DocumentBuilder.cpp @@ -96,13 +96,6 @@ nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement) PrefetchHref(hrefVal, aElement, hasPrefetch); } } - if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { - nsAutoString hrefVal; - aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); - if (!hrefVal.IsEmpty()) { - PrefetchDNS(hrefVal); - } - } } } From a5d37776674e0fc3b38f9120efbfdf6f3db1d7d0 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Dec 2015 14:21:57 -0500 Subject: [PATCH 019/102] Back out bug 1222524 for causing problems like bug 1227770 and bug 1227789. r=backout --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 56 ++----------------- gfx/layers/apz/src/AsyncPanZoomController.h | 16 ++---- gfx/layers/apz/src/OverscrollHandoffState.cpp | 24 -------- gfx/layers/apz/src/OverscrollHandoffState.h | 3 - 4 files changed, 10 insertions(+), 89 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index aa5ffcceda9e..52f5dc27e50d 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1223,8 +1223,6 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent case PANNING: case PANNING_LOCKED_X: case PANNING_LOCKED_Y: - case PANNING_LOCKED_X_SMOOTH_SCROLL: - case PANNING_LOCKED_Y_SMOOTH_SCROLL: case PINCHING: NS_WARNING("Received impossible touch in OnTouchStart"); break; @@ -1270,8 +1268,6 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) case PANNING: case PANNING_LOCKED_X: case PANNING_LOCKED_Y: - case PANNING_LOCKED_X_SMOOTH_SCROLL: - case PANNING_LOCKED_Y_SMOOTH_SCROLL: case PAN_MOMENTUM: TrackTouch(aEvent); return nsEventStatus_eConsumeNoDefault; @@ -1339,10 +1335,6 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) } return nsEventStatus_eIgnore; - case PANNING_LOCKED_X_SMOOTH_SCROLL: - case PANNING_LOCKED_Y_SMOOTH_SCROLL: - CancelAnimation(); - MOZ_FALLTHROUGH; case PANNING: case PANNING_LOCKED_X: case PANNING_LOCKED_Y: @@ -2148,13 +2140,11 @@ void AsyncPanZoomController::HandlePanning(double aAngle) { mY.SetAxisLocked(true); if (canScrollHorizontal) { SetState(PANNING_LOCKED_X); - overscrollHandoffChain->RequestSnapOnLock(Layer::VERTICAL); } } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) { mX.SetAxisLocked(true); if (canScrollVertical) { SetState(PANNING_LOCKED_Y); - overscrollHandoffChain->RequestSnapOnLock(Layer::HORIZONTAL); } } else { SetState(PANNING); @@ -2168,19 +2158,6 @@ void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi] angle = fabs(angle); // range [0, pi] - { - // If we can scroll on both axes, we want to allow breaking axis-lock. If - // we can only scroll on one axis, we want to always enable entering to - // axis-lock. - ReentrantMonitorAutoEnter lock(mMonitor); - if (!mX.CanScroll() || !mY.CanScroll()) { - if (mState == PANNING) { - HandlePanning(angle); - } - return; - } - } - float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI(); if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) { @@ -2450,20 +2427,7 @@ void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint } void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) { - // Only cancel animation and change state if the user isn't pan-locked. - // Snapping is requested during panning if the opposing axis of an - // unscrollable axis is locked. - if (mState == PANNING_LOCKED_X) { - mY.SetAxisLocked(false); - SetState(PANNING_LOCKED_X_SMOOTH_SCROLL); - } else if (mState == PANNING_LOCKED_Y) { - mX.SetAxisLocked(false); - SetState(PANNING_LOCKED_Y_SMOOTH_SCROLL); - } else { - CancelAnimation(); - SetState(SMOOTH_SCROLL); - } - + SetState(SMOOTH_SCROLL); nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to // appunits/second @@ -2878,15 +2842,7 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime, } } else { mAnimation = nullptr; - if (mState == PANNING_LOCKED_X_SMOOTH_SCROLL) { - mY.SetAxisLocked(true); - SetState(PANNING_LOCKED_X); - } else if (mState == PANNING_LOCKED_Y_SMOOTH_SCROLL) { - mX.SetAxisLocked(true); - SetState(PANNING_LOCKED_Y); - } else { - SetState(NOTHING); - } + SetState(NOTHING); RequestContentRepaint(); } UpdateSharedCompositorFrameMetrics(); @@ -3255,6 +3211,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri Stringify(mFrameMetrics.GetScrollOffset()).c_str(), Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str()); + CancelAnimation(); + // It's important to not send repaint requests between this // CopySmoothScrollInfo call and the sending of the scroll update // acknowledgement below, otherwise that repaint request will get rejected @@ -3485,10 +3443,7 @@ bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) { } bool AsyncPanZoomController::IsInPanningState() const { - return (mState == PANNING || - mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y || - mState == PANNING_LOCKED_X_SMOOTH_SCROLL || - mState == PANNING_LOCKED_Y_SMOOTH_SCROLL); + return (mState == PANNING || mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y); } void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) { @@ -3605,7 +3560,6 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() { void AsyncPanZoomController::RequestSnap() { if (RefPtr controller = GetGeckoContentController()) { - ReentrantMonitorAutoEnter lock(mMonitor); APZC_LOG("%p requesting snap near %s\n", this, Stringify(mFrameMetrics.GetScrollOffset()).c_str()); controller->RequestFlingSnap(mFrameMetrics.GetScrollId(), diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 8fe0c40e5aa2..a313c8c08031 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -393,11 +393,6 @@ public: void NotifyMozMouseScrollEvent(const nsString& aString) const; - // This is called to request that the main thread snap the scroll position - // to a nearby snap position if appropriate. The current scroll position is - // used as the final destination. - void RequestSnap(); - protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~AsyncPanZoomController(); @@ -649,6 +644,11 @@ protected: // Common processing at the end of a touch block. void OnTouchEndOrCancel(); + // This is called to request that the main thread snap the scroll position + // to a nearby snap position if appropriate. The current scroll position is + // used as the final destination. + void RequestSnap(); + uint64_t mLayersId; RefPtr mCompositorParent; RefPtr mPaintThrottler; @@ -760,12 +760,6 @@ protected: the finger is lifted. */ SMOOTH_SCROLL, /* Smooth scrolling to destination. Used by CSSOM-View smooth scroll-behavior */ - - PANNING_LOCKED_X_SMOOTH_SCROLL, /* Smooth scrolling animation initiated - while simultaneously panning the frame - with the X axis locked. */ - PANNING_LOCKED_Y_SMOOTH_SCROLL, /* as above for Y axis. */ - WHEEL_SCROLL /* Smooth scrolling to a destination for a wheel event. */ }; diff --git a/gfx/layers/apz/src/OverscrollHandoffState.cpp b/gfx/layers/apz/src/OverscrollHandoffState.cpp index 78e0fdab870c..150c52c6956a 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.cpp +++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp @@ -112,30 +112,6 @@ OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* a } } } -void -OverscrollHandoffChain::RequestSnapOnLock(Layer::ScrollDirection aAxis) const -{ - for (uint32_t i = 0; i < Length(); ++i) { - AsyncPanZoomController* apzc = mChain[i]; - if (!apzc->IsDestroyed()) { - switch (aAxis) { - case Layer::HORIZONTAL: - if (!apzc->CanScroll(Layer::VERTICAL)) { - apzc->RequestSnap(); - } - break; - case Layer::VERTICAL: - if (!apzc->CanScroll(Layer::HORIZONTAL)) { - apzc->RequestSnap(); - } - break; - default: - MOZ_ASSERT(false); - break; - } - } - } -} bool OverscrollHandoffChain::CanBePanned(const AsyncPanZoomController* aApzc) const diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h index 1738e8dd0257..e49c69b559f8 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.h +++ b/gfx/layers/apz/src/OverscrollHandoffState.h @@ -71,9 +71,6 @@ public: // |aStart| onwards, if any. void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const; - // Request snap on APZCs that cannot scroll on the opposing axis to |aAxis|. - void RequestSnapOnLock(Layer::ScrollDirection aAxis) const; - // Determine whether the given APZC, or any APZC further in the chain, // has room to be panned. bool CanBePanned(const AsyncPanZoomController* aApzc) const; From cb0bc4330510825057335ecad711af1b22bf7cdf Mon Sep 17 00:00:00 2001 From: philipp Date: Tue, 1 Dec 2015 12:40:25 -0700 Subject: [PATCH 020/102] Bug 1222819 - Add Orbit Downloader .dlls to Windows blocklist. r=aklotz --HG-- extra : rebase_source : af54cd732bc19f4b5b2a4a0a7b9fb05c9984c584 --- mozglue/build/WindowsDllBlocklist.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp index 319edb99bfe1..de47976cc9fc 100644 --- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/build/WindowsDllBlocklist.cpp @@ -183,6 +183,10 @@ static DllBlockInfo sWindowsDllBlocklist[] = { // NetOp School, discontinued product, bug 763395 { "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) }, + // Orbit Downloader, bug 1222819 + { "grabdll.dll", MAKE_VERSION(2, 6, 1, 0) }, + { "grabkernel.dll", MAKE_VERSION(1, 0, 0, 1) }, + { nullptr, 0 } }; From 50e8c44daf5686b968c960fbc5c810836eea7169 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 26 Nov 2015 14:44:55 -0500 Subject: [PATCH 021/102] Bug 1225576. Always use the VAO workaround. r=jgilbert I'd like to unconditionally use this work around for now. This bug affects the current version of ANGLE and older versions of Mesa (not just llvmpipe like we were checking for). Once ANGLE is fixed we can consider disabling this on Windows again. FWIW, Chrome does something like this unconditionally. --- dom/canvas/WebGLVertexArrayGL.cpp | 12 +----------- dom/canvas/WebGLVertexArrayGL.h | 4 +--- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/dom/canvas/WebGLVertexArrayGL.cpp b/dom/canvas/WebGLVertexArrayGL.cpp index e88c2a4b911a..2ba38330ecdf 100644 --- a/dom/canvas/WebGLVertexArrayGL.cpp +++ b/dom/canvas/WebGLVertexArrayGL.cpp @@ -12,9 +12,7 @@ namespace mozilla { WebGLVertexArrayGL::WebGLVertexArrayGL(WebGLContext* webgl) : WebGLVertexArray(webgl) -#if defined(XP_LINUX) , mIsVAO(false) -#endif { } WebGLVertexArrayGL::~WebGLVertexArrayGL() @@ -30,9 +28,7 @@ WebGLVertexArrayGL::DeleteImpl() mContext->MakeContextCurrent(); mContext->gl->fDeleteVertexArrays(1, &mGLName); -#if defined(XP_LINUX) mIsVAO = false; -#endif } void @@ -41,9 +37,7 @@ WebGLVertexArrayGL::BindVertexArrayImpl() mContext->mBoundVertexArray = this; mContext->gl->fBindVertexArray(mGLName); -#if defined(XP_LINUX) mIsVAO = true; -#endif } void @@ -55,15 +49,11 @@ WebGLVertexArrayGL::GenVertexArray() bool WebGLVertexArrayGL::IsVertexArrayImpl() { -#if defined(XP_LINUX) gl::GLContext* gl = mContext->gl; - if (gl->WorkAroundDriverBugs() && - gl->Vendor() == gl::GLVendor::VMware && - gl->Renderer() == gl::GLRenderer::GalliumLlvmpipe) + if (gl->WorkAroundDriverBugs()) { return mIsVAO; } -#endif mContext->MakeContextCurrent(); return mContext->gl->fIsVertexArray(mGLName) != 0; diff --git a/dom/canvas/WebGLVertexArrayGL.h b/dom/canvas/WebGLVertexArrayGL.h index 4923804347c2..c64291033e35 100644 --- a/dom/canvas/WebGLVertexArrayGL.h +++ b/dom/canvas/WebGLVertexArrayGL.h @@ -25,13 +25,11 @@ protected: explicit WebGLVertexArrayGL(WebGLContext* webgl); ~WebGLVertexArrayGL(); -#if defined(XP_LINUX) // Bug 1140459: Some drivers (including our test slaves!) don't - // give reasonable answers for IsRenderbuffer, maybe others. + // give reasonable answers for IsVertexArray, maybe others. // // So we track the `is a VAO` state ourselves. bool mIsVAO; -#endif }; } // namespace mozilla From 32869414c2d072232867a8a35638e658ba8f6e86 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 30 Nov 2015 10:31:12 -0500 Subject: [PATCH 022/102] Bug 1225280. Update ANGLE to chromium/2572. --- gfx/angle/AUTHORS | 1 + gfx/angle/BUILD.gn | 1 + gfx/angle/README.md | 23 +- gfx/angle/include/GLSLANG/ShaderLang.h | 4 +- gfx/angle/include/platform/Platform.h | 36 +- gfx/angle/moz.build | 2 +- gfx/angle/src/commit.h | 4 +- gfx/angle/src/common/angleutils.h | 4 +- gfx/angle/src/common/utilities.cpp | 25 - gfx/angle/src/compiler.gypi | 3 +- .../BuiltInFunctionEmulatorGLSL.cpp | 192 +- .../src/compiler/translator/Compiler.cpp | 11 +- .../src/compiler/translator/ExtensionGLSL.cpp | 100 + .../src/compiler/translator/ExtensionGLSL.h | 39 + .../src/compiler/translator/IntermNode.cpp | 292 ++- .../src/compiler/translator/IntermNode.h | 41 +- .../src/compiler/translator/Intermediate.cpp | 122 +- .../src/compiler/translator/Intermediate.h | 14 +- .../compiler/translator/OutputGLSLBase.cpp | 14 + .../src/compiler/translator/OutputGLSLBase.h | 1 + .../src/compiler/translator/OutputHLSL.cpp | 166 +- .../src/compiler/translator/OutputHLSL.h | 7 + .../src/compiler/translator/ParseContext.cpp | 638 ++--- .../src/compiler/translator/ParseContext.h | 63 +- .../translator/RemoveDynamicIndexing.cpp | 33 +- .../SeparateArrayInitialization.cpp | 8 +- .../translator/SeparateArrayInitialization.h | 5 + .../src/compiler/translator/SymbolTable.h | 32 +- .../compiler/translator/TranslatorGLSL.cpp | 39 +- .../src/compiler/translator/TranslatorGLSL.h | 2 +- gfx/angle/src/compiler/translator/Types.h | 4 + .../translator/UnfoldShortCircuitToIf.cpp | 228 +- .../src/compiler/translator/VariableInfo.cpp | 5 +- gfx/angle/src/compiler/translator/glslang.l | 28 +- gfx/angle/src/compiler/translator/glslang.y | 31 +- .../src/compiler/translator/glslang_lex.cpp | 28 +- .../src/compiler/translator/glslang_tab.cpp | 87 +- .../src/compiler/translator/parseConst.cpp | 265 --- gfx/angle/src/compiler/translator/util.cpp | 4 +- gfx/angle/src/compiler/translator/util.h | 4 +- gfx/angle/src/libANGLE/Caps.cpp | 156 +- gfx/angle/src/libANGLE/Caps.h | 15 + gfx/angle/src/libANGLE/Context.cpp | 124 +- gfx/angle/src/libANGLE/Context.h | 16 +- gfx/angle/src/libANGLE/Data.cpp | 23 +- gfx/angle/src/libANGLE/Data.h | 30 +- gfx/angle/src/libANGLE/Device.cpp | 5 +- gfx/angle/src/libANGLE/Error.cpp | 17 + gfx/angle/src/libANGLE/Error.h | 4 + gfx/angle/src/libANGLE/Platform.cpp | 6 +- gfx/angle/src/libANGLE/Program.cpp | 30 +- gfx/angle/src/libANGLE/Program.h | 18 - gfx/angle/src/libANGLE/RefCountObject.h | 17 + gfx/angle/src/libANGLE/Shader.cpp | 38 +- gfx/angle/src/libANGLE/Shader.h | 2 + gfx/angle/src/libANGLE/State.cpp | 30 +- gfx/angle/src/libANGLE/Surface_unittest.cpp | 47 +- gfx/angle/src/libANGLE/VertexArray.cpp | 1 - gfx/angle/src/libANGLE/angletypes.cpp | 24 + gfx/angle/src/libANGLE/angletypes.h | 14 + gfx/angle/src/libANGLE/features.h | 12 +- gfx/angle/src/libANGLE/formatutils.cpp | 52 +- gfx/angle/src/libANGLE/formatutils.h | 2 +- gfx/angle/src/libANGLE/moz.build | 16 +- gfx/angle/src/libANGLE/queryconversions.cpp | 34 +- gfx/angle/src/libANGLE/renderer/DeviceImpl.h | 2 +- .../libANGLE/renderer/FramebufferImpl_mock.h | 65 + .../src/libANGLE/renderer/ProgramImpl_mock.h | 64 + gfx/angle/src/libANGLE/renderer/ShaderImpl.h | 5 +- .../src/libANGLE/renderer/d3d/BufferD3D.cpp | 140 +- .../src/libANGLE/renderer/d3d/BufferD3D.h | 25 +- .../src/libANGLE/renderer/d3d/DeviceD3D.cpp | 22 +- .../src/libANGLE/renderer/d3d/DeviceD3D.h | 7 +- .../src/libANGLE/renderer/d3d/DisplayD3D.cpp | 12 +- .../src/libANGLE/renderer/d3d/DisplayD3D.h | 2 - .../src/libANGLE/renderer/d3d/DynamicHLSL.cpp | 1441 +++++------- .../src/libANGLE/renderer/d3d/DynamicHLSL.h | 95 +- .../renderer/d3d/DynamicHLSL_unittest.cpp | 30 - .../libANGLE/renderer/d3d/HLSLCompiler.cpp | 21 +- .../renderer/d3d/IndexDataManager.cpp | 136 +- .../libANGLE/renderer/d3d/IndexDataManager.h | 18 +- .../src/libANGLE/renderer/d3d/ProgramD3D.cpp | 912 +++++--- .../src/libANGLE/renderer/d3d/ProgramD3D.h | 159 +- .../src/libANGLE/renderer/d3d/RendererD3D.cpp | 57 +- .../src/libANGLE/renderer/d3d/RendererD3D.h | 76 +- .../src/libANGLE/renderer/d3d/ShaderD3D.cpp | 16 +- .../src/libANGLE/renderer/d3d/ShaderD3D.h | 25 +- .../src/libANGLE/renderer/d3d/TextureD3D.cpp | 3 - .../libANGLE/renderer/d3d/VaryingPacking.cpp | 397 ++++ .../libANGLE/renderer/d3d/VaryingPacking.h | 175 ++ .../libANGLE/renderer/d3d/VertexBuffer.cpp | 18 +- .../src/libANGLE/renderer/d3d/VertexBuffer.h | 6 + .../renderer/d3d/VertexDataManager.cpp | 53 +- .../libANGLE/renderer/d3d/VertexDataManager.h | 3 - .../libANGLE/renderer/d3d/d3d11/Blit11.cpp | 134 +- .../src/libANGLE/renderer/d3d/d3d11/Blit11.h | 4 + .../libANGLE/renderer/d3d/d3d11/Buffer11.cpp | 8 +- .../renderer/d3d/d3d11/DebugAnnotator11.cpp | 44 +- .../renderer/d3d/d3d11/InputLayoutCache.cpp | 4 + .../renderer/d3d/d3d11/Renderer11.cpp | 749 ++++-- .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 84 +- .../renderer/d3d/d3d11/StateManager11.cpp | 161 ++ .../renderer/d3d/d3d11/StateManager11.h | 51 + .../d3d/d3d11/gen_load_functions_table.py | 199 ++ .../d3d/d3d11/gen_swizzle_format_table.py | 133 ++ .../d3d/d3d11/gen_texture_format_table.py | 302 +++ .../internal_format_initializer_table.cpp | 170 ++ .../d3d11/internal_format_initializer_table.h | 31 + .../d3d/d3d11/load_functions_data.json | 1107 +++++++++ .../renderer/d3d/d3d11/load_functions_table.h | 30 + .../d3d11/load_functions_table_autogen.cpp | 2079 +++++++++++++++++ .../renderer/d3d/d3d11/renderer11_utils.cpp | 11 +- .../renderer/d3d/d3d11/renderer11_utils.h | 2 + .../d3d/d3d11/swizzle_format_data.json | 77 + .../d3d/d3d11/swizzle_format_info.cpp | 118 - .../renderer/d3d/d3d11/swizzle_format_info.h | 4 +- .../d3d/d3d11/swizzle_format_info_autogen.cpp | 203 ++ .../d3d/d3d11/texture_format_data.json | 889 +++++++ .../d3d/d3d11/texture_format_table.cpp | 23 +- .../d3d11/texture_format_table_autogen.cpp | 1775 ++++++++++++++ .../d3d/d3d11/texture_format_util.cpp | 29 +- .../renderer/d3d/d3d11/texture_format_util.h | 39 - .../src/libANGLE/renderer/d3d/d3d9/Blit9.cpp | 83 +- .../libANGLE/renderer/d3d/d3d9/Buffer9.cpp | 6 +- .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 141 +- .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 59 +- .../renderer/d3d/d3d9/renderer9_utils.cpp | 1 + .../libANGLE/renderer/d3d/loadimage_etc.cpp | 953 ++++++++ .../src/libANGLE/renderer/d3d/loadimage_etc.h | 130 ++ gfx/angle/src/libANGLE/renderer/gl/BlitGL.cpp | 63 +- gfx/angle/src/libANGLE/renderer/gl/BlitGL.h | 2 +- .../src/libANGLE/renderer/gl/FunctionsGL.cpp | 28 +- .../src/libANGLE/renderer/gl/ProgramGL.cpp | 23 + .../src/libANGLE/renderer/gl/RendererGL.cpp | 5 +- .../src/libANGLE/renderer/gl/ShaderGL.cpp | 4 +- gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h | 3 +- .../libANGLE/renderer/gl/StateManagerGL.cpp | 86 +- .../src/libANGLE/renderer/gl/StateManagerGL.h | 6 + .../renderer/gl/TransformFeedbackGL.cpp | 115 +- .../renderer/gl/TransformFeedbackGL.h | 23 +- .../libANGLE/renderer/gl/VertexArrayGL.cpp | 2 +- .../libANGLE/renderer/gl/cgl/DisplayCGL.mm | 8 +- .../libANGLE/renderer/gl/formatutilsgl.cpp | 47 +- .../libANGLE/renderer/gl/glx/DisplayGLX.cpp | 294 ++- .../src/libANGLE/renderer/gl/glx/DisplayGLX.h | 38 + .../renderer/gl/glx/FBConfigCompatibility.md | 32 + .../libANGLE/renderer/gl/glx/FunctionsGLX.cpp | 93 +- .../libANGLE/renderer/gl/glx/FunctionsGLX.h | 9 + .../renderer/gl/glx/PbufferSurfaceGLX.cpp | 10 +- .../renderer/gl/glx/WindowSurfaceGLX.cpp | 22 +- .../renderer/gl/glx/WindowSurfaceGLX.h | 6 +- .../renderer/gl/glx/functionsglx_typedefs.h | 2 + .../libANGLE/renderer/gl/glx/platform_glx.h | 5 + .../libANGLE/renderer/gl/renderergl_utils.cpp | 36 +- .../libANGLE/renderer/gl/wgl/DisplayWGL.cpp | 24 +- .../libANGLE/renderer/gl/wgl/FunctionsWGL.cpp | 2 + .../libANGLE/renderer/gl/wgl/wgl_utils.cpp | 2 +- gfx/angle/src/libANGLE/validationES.cpp | 71 +- gfx/angle/src/libANGLE/validationES.h | 10 +- gfx/angle/src/libANGLE/validationES2.cpp | 83 + gfx/angle/src/libANGLE/validationES2.h | 5 + gfx/angle/src/libANGLE/validationES3.cpp | 77 +- gfx/angle/src/libANGLE/validationES3.h | 5 + .../src/libANGLE/validationES_unittest.cpp | 145 ++ gfx/angle/src/libEGL/moz.build | 4 +- gfx/angle/src/libGLESv2.gypi | 20 +- gfx/angle/src/libGLESv2/entry_points_egl.cpp | 38 +- .../src/libGLESv2/entry_points_egl_ext.cpp | 9 +- .../src/libGLESv2/entry_points_gles_2_0.cpp | 2 +- .../libGLESv2/entry_points_gles_2_0_ext.cpp | 83 +- .../src/libGLESv2/entry_points_gles_2_0_ext.h | 6 + .../src/libGLESv2/entry_points_gles_3_0.cpp | 34 +- gfx/angle/src/libGLESv2/global_state.cpp | 15 +- gfx/angle/src/libGLESv2/libGLESv2.cpp | 20 + gfx/angle/src/libGLESv2/libGLESv2.def | 4 + gfx/angle/src/libGLESv2/moz.build | 4 +- gfx/angle/src/tests/BUILD.gn | 150 +- .../src/tests/WebGL-CTS-known-failures.txt | 6 +- gfx/angle/src/tests/angle_end2end_tests.gypi | 1 + gfx/angle/src/tests/angle_perftests.gypi | 1 + gfx/angle/src/tests/angle_unittests.gypi | 4 +- .../compiler_tests/ConstantFolding_test.cpp | 266 ++- .../compiler_tests/MalformedShader_test.cpp | 399 ++++ .../tests/deqp_support/angle_deqp_gtest.cpp | 10 - .../angle_deqp_libtester_main.cpp | 3 +- .../deqp_egl_test_expectations.txt | 2 - .../deqp_gles2_test_expectations.txt | 8 +- .../deqp_gles3_test_expectations.txt | 163 +- .../src/tests/egl_tests/EGLSurfaceTest.cpp | 4 + .../D3D11EmulatedIndexedBufferTest.cpp | 2 +- .../gl_tests/DifferentStencilMasksTest.cpp | 178 ++ .../gl_tests/DiscardFramebufferEXTTest.cpp | 7 +- gfx/angle/src/tests/gl_tests/GLSLTest.cpp | 263 ++- .../src/tests/gl_tests/ProgramBinaryTest.cpp | 6 +- .../tests/gl_tests/ProvokingVertexTest.cpp | 305 +++ .../tests/gl_tests/TransformFeedbackTest.cpp | 493 +++- gfx/angle/src/tests/gl_tests/UniformTest.cpp | 11 +- .../src/tests/perf_tests/BufferSubData.cpp | 2 +- .../src/tests/perf_tests/DrawCallPerf.cpp | 2 +- .../tests/perf_tests/IndexDataManagerTest.cpp | 3 +- .../perf_tests/InterleavedAttributeData.cpp | 236 ++ .../src/tests/perf_tests/PointSprites.cpp | 2 +- .../src/tests/perf_tests/TexSubImage.cpp | 2 +- gfx/angle/src/tests/test_utils/ANGLETest.cpp | 37 +- gfx/angle/src/tests/test_utils/ANGLETest.h | 7 + .../src/tests/test_utils/compiler_test.cpp | 5 - 206 files changed, 17120 insertions(+), 4240 deletions(-) create mode 100644 gfx/angle/src/compiler/translator/ExtensionGLSL.cpp create mode 100644 gfx/angle/src/compiler/translator/ExtensionGLSL.h delete mode 100644 gfx/angle/src/compiler/translator/parseConst.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h create mode 100644 gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h delete mode 100644 gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL_unittest.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.h create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_swizzle_format_table.py create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_data.json delete mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info_autogen.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp delete mode 100644 gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.h create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp create mode 100644 gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h create mode 100644 gfx/angle/src/libANGLE/validationES_unittest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/DifferentStencilMasksTest.cpp create mode 100644 gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp create mode 100644 gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp diff --git a/gfx/angle/AUTHORS b/gfx/angle/AUTHORS index 836bed8e8ab7..8de8fbddf6af 100644 --- a/gfx/angle/AUTHORS +++ b/gfx/angle/AUTHORS @@ -41,3 +41,4 @@ Aitor Moreno Yuri O'Donnell Josh Soref Maks Naumov +Jinyoung Hur diff --git a/gfx/angle/BUILD.gn b/gfx/angle/BUILD.gn index 28f23ff9427b..39bee5abaa92 100644 --- a/gfx/angle/BUILD.gn +++ b/gfx/angle/BUILD.gn @@ -379,6 +379,7 @@ static_library("angle_util") { if (is_linux) { sources += rebase_path(util_gypi.util_linux_sources, ".", "util") + libs = [ "rt" ] } if (is_mac) { diff --git a/gfx/angle/README.md b/gfx/angle/README.md index c85fa8244ba3..d2c28c5ef5f4 100644 --- a/gfx/angle/README.md +++ b/gfx/angle/README.md @@ -1,7 +1,20 @@ #ANGLE -The goal of ANGLE is to allow Windows users to seamlessly run WebGL and other OpenGL ES content by translating OpenGL ES API calls to DirectX 9 or DirectX 11 API calls. +The goal of ANGLE is to allow users of multiple operating systems to seamlessly run WebGL and other OpenGL ES content by translating OpenGL ES API calls to one of the hardware-supported APIs available for that platform. ANGLE currently provides translation from OpenGL ES 2.0 to desktop OpenGL, Direct3D 9, and Direct3D 11. Support for translation from OpenGL ES 3.0 to all of these APIs is nearing completion, and future plans include enabling validated ES-to-ES support. -ANGLE is a conformant implementation of the OpenGL ES 2.0 specification that is hardwareâ€accelerated via Direct3D. ANGLE v1.0.772 was certified compliant by passing the ES 2.0.3 conformance tests in October 2011. ANGLE also provides an implementation of the EGL 1.4 specification. Work on ANGLE's OpenGL ES 3.0 implementation is currently in progress, but should not be considered stable. +| | Direct3D 9 | Direct3D 11 | Desktop GL | GL ES | +|----------------|:-------------:|:-------------------:|:------------------:|:---------:| +| OpenGL ES 2.0 | complete | complete | complete | planned | +| OpenGL ES 3.0 | | nearing completion | nearing completion | planned | +[Level of OpenGL ES support via backing renderers] + +| | Direct3D 9 | Direct3D 11 | Desktop GL | +|------------:|:--------------:|:--------------:|:-------------:| +| Windows | * | * | * | +| Linux | | | * | +| Mac OS X | | | in progress | +[Platform support via backing renderers] + +ANGLE v1.0.772 was certified compliant by passing the ES 2.0.3 conformance tests in October 2011. ANGLE also provides an implementation of the EGL 1.4 specification. ANGLE is used as the default WebGL backend for both Google Chrome and Mozilla Firefox on Windows platforms. Chrome uses ANGLE for all graphics rendering on Windows, including the accelerated Canvas2D implementation and the Native Client sandbox environment. @@ -15,8 +28,12 @@ View the [Dev setup instructions](doc/DevSetup.md). * Join us on IRC in the #ANGLEproject channel on FreeNode. * Read about ANGLE development in our [documentation](doc). * Become a [code contributor](doc/ContributingCode.md). +* Refer to ANGLE's [coding standard](doc/CodingStandard.md). +* Learn how to [build ANGLE for Chromium development](doc/BuildingAngleForChromiumDevelopment.md). +* [Choose an ANGLE branch](doc/ChoosingANGLEBranch.md) to track in your own project. * File bugs in the [issue tracker](http://code.google.com/p/angleproject/issues/list) (preferably with an isolated test-case). * Read about WebGL on the [Khronos WebGL Wiki](http://khronos.org/webgl/wiki/Main_Page). * Learn about implementation details in the [OpenGL Insights chapter on ANGLE](http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-ANGLE.pdf) and this [ANGLE presentation](https://code.google.com/p/angleproject/downloads/detail?name=ANGLE%20and%20Cross-Platform%20WebGL%20Support.pdf&can=2&q=). * Learn about the past, present, and future of the ANGLE implementation in [this recent presentation](https://docs.google.com/presentation/d/1CucIsdGVDmdTWRUbg68IxLE5jXwCb2y1E9YVhQo0thg/pub?start=false&loop=false). -* If you use ANGLE in your own project, we'd love to hear about it! \ No newline at end of file +* If you use ANGLE in your own project, we'd love to hear about it! + diff --git a/gfx/angle/include/GLSLANG/ShaderLang.h b/gfx/angle/include/GLSLANG/ShaderLang.h index 444a620f12ed..1534f72f7e45 100644 --- a/gfx/angle/include/GLSLANG/ShaderLang.h +++ b/gfx/angle/include/GLSLANG/ShaderLang.h @@ -266,7 +266,9 @@ typedef struct // function. This applies to Tegra K1 devices. int NV_draw_buffers; - // Set to 1 if highp precision is supported in the fragment language. + // Set to 1 if highp precision is supported in the ESSL 1.00 version of the + // fragment language. Does not affect versions of the language where highp + // support is mandatory. // Default is 0. int FragmentPrecisionHigh; diff --git a/gfx/angle/include/platform/Platform.h b/gfx/angle/include/platform/Platform.h index 4225ca0fc0b0..0e8cdf477ffe 100644 --- a/gfx/angle/include/platform/Platform.h +++ b/gfx/angle/include/platform/Platform.h @@ -11,7 +11,24 @@ #include -#include "../export.h" +#if defined(_WIN32) +# if !defined(LIBANGLE_IMPLEMENTATION) +# define ANGLE_PLATFORM_EXPORT __declspec(dllimport) +# endif +#elif defined(__GNUC__) +# if defined(LIBANGLE_IMPLEMENTATION) +# define ANGLE_PLATFORM_EXPORT __attribute__((visibility ("default"))) +# endif +#endif +#if !defined(ANGLE_PLATFORM_EXPORT) +# define ANGLE_PLATFORM_EXPORT +#endif + +#if defined(_WIN32) +# define ANGLE_APIENTRY __stdcall +#else +# define ANGLE_APIENTRY +#endif namespace angle { @@ -134,13 +151,18 @@ class Platform } -typedef void(*ANGLEPlatformInitializeFunc)(angle::Platform*); -ANGLE_EXPORT void ANGLEPlatformInitialize(angle::Platform*); +extern "C" +{ -typedef void (*ANGLEPlatformShutdownFunc)(); -ANGLE_EXPORT void ANGLEPlatformShutdown(); +typedef void (ANGLE_APIENTRY *ANGLEPlatformInitializeFunc)(angle::Platform*); +ANGLE_PLATFORM_EXPORT void ANGLE_APIENTRY ANGLEPlatformInitialize(angle::Platform*); -typedef angle::Platform *(*ANGLEPlatformCurrentFunc)(); -ANGLE_EXPORT angle::Platform *ANGLEPlatformCurrent(); +typedef void (ANGLE_APIENTRY *ANGLEPlatformShutdownFunc)(); +ANGLE_PLATFORM_EXPORT void ANGLE_APIENTRY ANGLEPlatformShutdown(); + +typedef angle::Platform *(ANGLE_APIENTRY *ANGLEPlatformCurrentFunc)(); +ANGLE_PLATFORM_EXPORT angle::Platform *ANGLE_APIENTRY ANGLEPlatformCurrent(); + +} #endif // ANGLE_PLATFORM_H diff --git a/gfx/angle/moz.build b/gfx/angle/moz.build index a13e401542ce..cefe4960358f 100644 --- a/gfx/angle/moz.build +++ b/gfx/angle/moz.build @@ -46,6 +46,7 @@ UNIFIED_SOURCES += [ 'src/compiler/translator/Diagnostics.cpp', 'src/compiler/translator/DirectiveHandler.cpp', 'src/compiler/translator/EmulatePrecision.cpp', + 'src/compiler/translator/ExtensionGLSL.cpp', 'src/compiler/translator/FlagStd140Structs.cpp', 'src/compiler/translator/ForLoopUnroll.cpp', 'src/compiler/translator/InfoSink.cpp', @@ -63,7 +64,6 @@ UNIFIED_SOURCES += [ 'src/compiler/translator/OutputGLSL.cpp', 'src/compiler/translator/OutputGLSLBase.cpp', 'src/compiler/translator/OutputHLSL.cpp', - 'src/compiler/translator/parseConst.cpp', 'src/compiler/translator/ParseContext.cpp', 'src/compiler/translator/PoolAlloc.cpp', 'src/compiler/translator/PruneEmptyDeclarations.cpp', diff --git a/gfx/angle/src/commit.h b/gfx/angle/src/commit.h index 08c200311a4e..42cf1745fee5 100644 --- a/gfx/angle/src/commit.h +++ b/gfx/angle/src/commit.h @@ -1,3 +1,3 @@ -#define ANGLE_COMMIT_HASH "c7fc1b46df29" +#define ANGLE_COMMIT_HASH "2eb89424cc6d" #define ANGLE_COMMIT_HASH_SIZE 12 -#define ANGLE_COMMIT_DATE "2015-11-09 16:31:17 -0500" +#define ANGLE_COMMIT_DATE "2015-11-28 17:17:38 -0500" diff --git a/gfx/angle/src/common/angleutils.h b/gfx/angle/src/common/angleutils.h index f3d201993a71..c50a78eb5943 100644 --- a/gfx/angle/src/common/angleutils.h +++ b/gfx/angle/src/common/angleutils.h @@ -70,9 +70,9 @@ void SafeDelete(T*& resource) template void SafeDeleteContainer(T& resource) { - for (typename T::iterator i = resource.begin(); i != resource.end(); i++) + for (auto &element : resource) { - SafeDelete(*i); + SafeDelete(element); } resource.clear(); } diff --git a/gfx/angle/src/common/utilities.cpp b/gfx/angle/src/common/utilities.cpp index 3952872cf0b7..2ab913b10fff 100644 --- a/gfx/angle/src/common/utilities.cpp +++ b/gfx/angle/src/common/utilities.cpp @@ -797,32 +797,7 @@ void writeFile(const char* path, const void* content, size_t size) // to run, the function returns immediately, and the thread continues execution. void ScheduleYield() { -#if defined(ANGLE_ENABLE_WINDOWS_STORE) - // This implementation of Sleep exists because it is not available prior to Update 4. - static HANDLE singletonEvent = nullptr; - HANDLE sleepEvent = singletonEvent; - if (!sleepEvent) - { - sleepEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); - - if (!sleepEvent) - return; - - HANDLE previousEvent = InterlockedCompareExchangePointerRelease(&singletonEvent, sleepEvent, nullptr); - - if (previousEvent) - { - // Back out if multiple threads try to demand create at the same time. - CloseHandle(sleepEvent); - sleepEvent = previousEvent; - } - } - - // Emulate sleep by waiting with timeout on an event that is never signalled. - WaitForSingleObjectEx(sleepEvent, 0, false); -#else Sleep(0); -#endif } #endif diff --git a/gfx/angle/src/compiler.gypi b/gfx/angle/src/compiler.gypi index cecd2d730043..b2c590cfed83 100644 --- a/gfx/angle/src/compiler.gypi +++ b/gfx/angle/src/compiler.gypi @@ -43,6 +43,8 @@ 'compiler/translator/EmulatePrecision.cpp', 'compiler/translator/EmulatePrecision.h', 'compiler/translator/ExtensionBehavior.h', + 'compiler/translator/ExtensionGLSL.cpp', + 'compiler/translator/ExtensionGLSL.h', 'compiler/translator/FlagStd140Structs.cpp', 'compiler/translator/FlagStd140Structs.h', 'compiler/translator/ForLoopUnroll.cpp', @@ -137,7 +139,6 @@ 'compiler/translator/glslang_tab.h', 'compiler/translator/intermOut.cpp', 'compiler/translator/length_limits.h', - 'compiler/translator/parseConst.cpp', 'compiler/translator/timing/RestrictFragmentShaderTiming.cpp', 'compiler/translator/timing/RestrictFragmentShaderTiming.h', 'compiler/translator/timing/RestrictVertexShaderTiming.cpp', diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp index 6d4a811bb3de..1e9af7bae0e6 100644 --- a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp +++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp @@ -50,117 +50,141 @@ void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator const TType *float2 = TCache::getType(EbtFloat, 2); const TType *uint1 = TCache::getType(EbtUInt); + // clang-format off emu->addEmulatedFunction(EOpPackSnorm2x16, float2, "uint webgl_packSnorm2x16_emu(vec2 v)\n" "{\n" - " int x = int(round(clamp(v.x, -1.0, 1.0) * 32767.0));\n" - " int y = int(round(clamp(v.y, -1.0, 1.0) * 32767.0));\n" - " return uint((y << 16) | (x & 0xFFFF));\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return packSnorm2x16(v);\n" + " #else\n" + " int x = int(round(clamp(v.x, -1.0, 1.0) * 32767.0));\n" + " int y = int(round(clamp(v.y, -1.0, 1.0) * 32767.0));\n" + " return uint((y << 16) | (x & 0xFFFF));\n" + " #endif\n" "}\n"); emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1, - "float webgl_fromSnorm(uint x)\n" - "{\n" - " int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n" - " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n" - "}\n" + "#if !defined(GL_ARB_shading_language_packing)\n" + " float webgl_fromSnorm(uint x)\n" + " {\n" + " int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n" + " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n" + " }\n" + "#endif\n" "\n" "vec2 webgl_unpackSnorm2x16_emu(uint u)\n" "{\n" - " uint y = (u >> 16);\n" - " uint x = u;\n" - " return vec2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return unpackSnorm2x16(u);\n" + " #else\n" + " uint y = (u >> 16);\n" + " uint x = u;\n" + " return vec2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n" + " #endif\n" "}\n"); // Functions uint webgl_f32tof16(float val) and float webgl_f16tof32(uint val) are // based on the OpenGL redbook Appendix Session "Floating-Point Formats Used in OpenGL". emu->addEmulatedFunction(EOpPackHalf2x16, float2, - "uint webgl_f32tof16(float val)\n" - "{\n" - " uint f32 = floatBitsToUint(val);\n" - " uint f16 = 0u;\n" - " uint sign = (f32 >> 16) & 0x8000u;\n" - " int exponent = int((f32 >> 23) & 0xFFu) - 127;\n" - " uint mantissa = f32 & 0x007FFFFFu;\n" - " if (exponent == 128)\n" + "#if !defined(GL_ARB_shading_language_packing)\n" + " uint webgl_f32tof16(float val)\n" " {\n" - " // Infinity or NaN\n" - " // NaN bits that are masked out by 0x3FF get discarded.\n" - " // This can turn some NaNs to infinity, but this is allowed by the spec.\n" - " f16 = sign | (0x1Fu << 10);\n" - " f16 |= (mantissa & 0x3FFu);\n" + " uint f32 = floatBitsToUint(val);\n" + " uint f16 = 0u;\n" + " uint sign = (f32 >> 16) & 0x8000u;\n" + " int exponent = int((f32 >> 23) & 0xFFu) - 127;\n" + " uint mantissa = f32 & 0x007FFFFFu;\n" + " if (exponent == 128)\n" + " {\n" + " // Infinity or NaN\n" + " // NaN bits that are masked out by 0x3FF get discarded.\n" + " // This can turn some NaNs to infinity, but this is allowed by the spec.\n" + " f16 = sign | (0x1Fu << 10);\n" + " f16 |= (mantissa & 0x3FFu);\n" + " }\n" + " else if (exponent > 15)\n" + " {\n" + " // Overflow - flush to Infinity\n" + " f16 = sign | (0x1Fu << 10);\n" + " }\n" + " else if (exponent > -15)\n" + " {\n" + " // Representable value\n" + " exponent += 15;\n" + " mantissa >>= 13;\n" + " f16 = sign | uint(exponent << 10) | mantissa;\n" + " }\n" + " else\n" + " {\n" + " f16 = sign;\n" + " }\n" + " return f16;\n" " }\n" - " else if (exponent > 15)\n" - " {\n" - " // Overflow - flush to Infinity\n" - " f16 = sign | (0x1Fu << 10);\n" - " }\n" - " else if (exponent > -15)\n" - " {\n" - " // Representable value\n" - " exponent += 15;\n" - " mantissa >>= 13;\n" - " f16 = sign | uint(exponent << 10) | mantissa;\n" - " }\n" - " else\n" - " {\n" - " f16 = sign;\n" - " }\n" - " return f16;\n" - "}\n" + "#endif\n" "\n" "uint webgl_packHalf2x16_emu(vec2 v)\n" "{\n" - " uint x = webgl_f32tof16(v.x);\n" - " uint y = webgl_f32tof16(v.y);\n" - " return (y << 16) | x;\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return packHalf2x16(v);\n" + " #else\n" + " uint x = webgl_f32tof16(v.x);\n" + " uint y = webgl_f32tof16(v.y);\n" + " return (y << 16) | x;\n" + " #endif\n" "}\n"); emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1, - "float webgl_f16tof32(uint val)\n" - "{\n" - " uint sign = (val & 0x8000u) << 16;\n" - " int exponent = int((val & 0x7C00u) >> 10);\n" - " uint mantissa = val & 0x03FFu;\n" - " float f32 = 0.0;\n" - " if(exponent == 0)\n" + "#if !defined(GL_ARB_shading_language_packing)\n" + " float webgl_f16tof32(uint val)\n" " {\n" - " if (mantissa != 0u)\n" + " uint sign = (val & 0x8000u) << 16;\n" + " int exponent = int((val & 0x7C00u) >> 10);\n" + " uint mantissa = val & 0x03FFu;\n" + " float f32 = 0.0;\n" + " if(exponent == 0)\n" " {\n" - " const float scale = 1.0 / (1 << 24);\n" - " f32 = scale * mantissa;\n" + " if (mantissa != 0u)\n" + " {\n" + " const float scale = 1.0 / (1 << 24);\n" + " f32 = scale * mantissa;\n" + " }\n" + " }\n" + " else if (exponent == 31)\n" + " {\n" + " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n" + " }\n" + " else\n" + " {\n" + " exponent -= 15;\n" + " float scale;\n" + " if(exponent < 0)\n" + " {\n" + " scale = 1.0 / (1 << -exponent);\n" + " }\n" + " else\n" + " {\n" + " scale = 1 << exponent;\n" + " }\n" + " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n" + " f32 = scale * decimal;\n" " }\n" - " }\n" - " else if (exponent == 31)\n" - " {\n" - " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n" - " }\n" - " else\n" - " {\n" - " exponent -= 15;\n" - " float scale;\n" - " if(exponent < 0)\n" - " {\n" - " scale = 1.0 / (1 << -exponent);\n" - " }\n" - " else\n" - " {\n" - " scale = 1 << exponent;\n" - " }\n" - " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n" - " f32 = scale * decimal;\n" - " }\n" "\n" - " if (sign != 0u)\n" - " {\n" - " f32 = -f32;\n" - " }\n" + " if (sign != 0u)\n" + " {\n" + " f32 = -f32;\n" + " }\n" "\n" - " return f32;\n" - "}\n" + " return f32;\n" + " }\n" + "#endif\n" "\n" "vec2 webgl_unpackHalf2x16_emu(uint u)\n" "{\n" - " uint y = (u >> 16);\n" - " uint x = u & 0xFFFFu;\n" - " return vec2(webgl_f16tof32(x), webgl_f16tof32(y));\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return unpackHalf2x16(u);\n" + " #else\n" + " uint y = (u >> 16);\n" + " uint x = u & 0xFFFFu;\n" + " return vec2(webgl_f16tof32(x), webgl_f16tof32(y));\n" + " #endif\n" "}\n"); + // clang-format on } } diff --git a/gfx/angle/src/compiler/translator/Compiler.cpp b/gfx/angle/src/compiler/translator/Compiler.cpp index 9707250da65c..9002ce65fa31 100644 --- a/gfx/angle/src/compiler/translator/Compiler.cpp +++ b/gfx/angle/src/compiler/translator/Compiler.cpp @@ -213,13 +213,11 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], ++firstSource; } - bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1; TIntermediate intermediate(infoSink); - TParseContext parseContext(symbolTable, extensionBehavior, intermediate, - shaderType, shaderSpec, compileOptions, true, - infoSink, debugShaderPrecision); + TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec, + compileOptions, true, infoSink, getResources()); - parseContext.setFragmentPrecisionHigh(fragmentPrecisionHigh); + parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); SetGlobalParseContext(&parseContext); // We preserve symbols at the built-in level from compile-to-compile. @@ -252,6 +250,9 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], root = parseContext.getTreeRoot(); root = intermediate.postProcess(root); + // Highp might have been auto-enabled based on shader version + fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh(); + // Disallow expressions deemed too complex. if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) success = limitExpressionComplexity(root); diff --git a/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp b/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp new file mode 100644 index 000000000000..d7f45f7eef62 --- /dev/null +++ b/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp @@ -0,0 +1,100 @@ +// +// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ExtensionGLSL.cpp: Implements the TExtensionGLSL class that tracks GLSL extension requirements +// of shaders. + +#include "compiler/translator/ExtensionGLSL.h" + +#include "compiler/translator/VersionGLSL.h" + +TExtensionGLSL::TExtensionGLSL(ShShaderOutput output) + : TIntermTraverser(true, false, false), mTargetVersion(ShaderOutputTypeToGLSLVersion(output)) +{ +} + +const std::set &TExtensionGLSL::getEnabledExtensions() const +{ + return mEnabledExtensions; +} + +const std::set &TExtensionGLSL::getRequiredExtensions() const +{ + return mRequiredExtensions; +} + +bool TExtensionGLSL::visitUnary(Visit, TIntermUnary *node) +{ + checkOperator(node); + + return true; +} + +bool TExtensionGLSL::visitAggregate(Visit, TIntermAggregate *node) +{ + checkOperator(node); + + return true; +} + +void TExtensionGLSL::checkOperator(TIntermOperator *node) +{ + if (mTargetVersion < GLSL_VERSION_130) + { + return; + } + + switch (node->getOp()) + { + case EOpAbs: + break; + + case EOpSign: + break; + + case EOpMix: + break; + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + if (mTargetVersion < GLSL_VERSION_330) + { + // Bit conversion functions cannot be emulated. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + break; + + case EOpPackSnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackSnorm2x16: + case EOpUnpackHalf2x16: + if (mTargetVersion < GLSL_VERSION_420) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + + if (mTargetVersion < GLSL_VERSION_330) + { + // floatBitsToUint and uintBitsToFloat are needed to emulate + // packHalf2x16 and unpackHalf2x16 respectively and cannot be + // emulated themselves. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + } + break; + + case EOpPackUnorm2x16: + case EOpUnpackUnorm2x16: + if (mTargetVersion < GLSL_VERSION_410) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + } + break; + + default: + break; + } +} diff --git a/gfx/angle/src/compiler/translator/ExtensionGLSL.h b/gfx/angle/src/compiler/translator/ExtensionGLSL.h new file mode 100644 index 000000000000..6bb84d612daf --- /dev/null +++ b/gfx/angle/src/compiler/translator/ExtensionGLSL.h @@ -0,0 +1,39 @@ +// +// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ExtensionGLSL.h: Defines the TExtensionGLSL class that tracks GLSL extension requirements of +// shaders. + +#ifndef COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ +#define COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ + +#include +#include + +#include "compiler/translator/IntermNode.h" + +// Traverses the intermediate tree to determine which GLSL extensions are required +// to support the shader. +class TExtensionGLSL : public TIntermTraverser +{ + public: + TExtensionGLSL(ShShaderOutput output); + + const std::set &getEnabledExtensions() const; + const std::set &getRequiredExtensions() const; + + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + + private: + void checkOperator(TIntermOperator *node); + + int mTargetVersion; + + std::set mEnabledExtensions; + std::set mRequiredExtensions; +}; + +#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ diff --git a/gfx/angle/src/compiler/translator/IntermNode.cpp b/gfx/angle/src/compiler/translator/IntermNode.cpp index 0a089c66769b..2a9286008892 100644 --- a/gfx/angle/src/compiler/translator/IntermNode.cpp +++ b/gfx/angle/src/compiler/translator/IntermNode.cpp @@ -66,73 +66,6 @@ bool ValidateMultiplication(TOperator op, const TType &left, const TType &right) } } -bool CompareStructure(const TType& leftNodeType, - const TConstantUnion *rightUnionArray, - const TConstantUnion *leftUnionArray); - -bool CompareStruct(const TType &leftNodeType, - const TConstantUnion *rightUnionArray, - const TConstantUnion *leftUnionArray) -{ - const TFieldList &fields = leftNodeType.getStruct()->fields(); - - size_t structSize = fields.size(); - size_t index = 0; - - for (size_t j = 0; j < structSize; j++) - { - size_t size = fields[j]->type()->getObjectSize(); - for (size_t i = 0; i < size; i++) - { - if (fields[j]->type()->getBasicType() == EbtStruct) - { - if (!CompareStructure(*fields[j]->type(), - &rightUnionArray[index], - &leftUnionArray[index])) - { - return false; - } - } - else - { - if (leftUnionArray[index] != rightUnionArray[index]) - return false; - index++; - } - } - } - return true; -} - -bool CompareStructure(const TType &leftNodeType, - const TConstantUnion *rightUnionArray, - const TConstantUnion *leftUnionArray) -{ - if (leftNodeType.isArray()) - { - TType typeWithoutArrayness = leftNodeType; - typeWithoutArrayness.clearArrayness(); - - size_t arraySize = leftNodeType.getArraySize(); - - for (size_t i = 0; i < arraySize; ++i) - { - size_t offset = typeWithoutArrayness.getObjectSize() * i; - if (!CompareStruct(typeWithoutArrayness, - &rightUnionArray[offset], - &leftUnionArray[offset])) - { - return false; - } - } - } - else - { - return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray); - } - return true; -} - TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size) { TConstantUnion *constUnion = new TConstantUnion[size]; @@ -169,7 +102,7 @@ void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicTy } } -float VectorLength(TConstantUnion *paramArray, size_t paramArraySize) +float VectorLength(const TConstantUnion *paramArray, size_t paramArraySize) { float result = 0.0f; for (size_t i = 0; i < paramArraySize; i++) @@ -180,7 +113,9 @@ float VectorLength(TConstantUnion *paramArray, size_t paramArraySize) return sqrtf(result); } -float VectorDotProduct(TConstantUnion *paramArray1, TConstantUnion *paramArray2, size_t paramArraySize) +float VectorDotProduct(const TConstantUnion *paramArray1, + const TConstantUnion *paramArray2, + size_t paramArraySize) { float result = 0.0f; for (size_t i = 0; i < paramArraySize; i++) @@ -188,19 +123,23 @@ float VectorDotProduct(TConstantUnion *paramArray1, TConstantUnion *paramArray2, return result; } -TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, const TIntermTyped *originalNode) +TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, + const TIntermTyped *originalNode, + TQualifier qualifier) { if (constArray == nullptr) { return nullptr; } TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType()); - folded->getTypePointer()->setQualifier(EvqConst); + folded->getTypePointer()->setQualifier(qualifier); folded->setLine(originalNode->getLine()); return folded; } -angle::Matrix GetMatrix(TConstantUnion *paramArray, const unsigned int &rows, const unsigned int &cols) +angle::Matrix GetMatrix(const TConstantUnion *paramArray, + const unsigned int &rows, + const unsigned int &cols) { std::vector elements; for (size_t i = 0; i < rows * cols; i++) @@ -210,7 +149,7 @@ angle::Matrix GetMatrix(TConstantUnion *paramArray, const unsigned int &r return angle::Matrix(elements, rows, cols).transpose(); } -angle::Matrix GetMatrix(TConstantUnion *paramArray, const unsigned int &size) +angle::Matrix GetMatrix(const TConstantUnion *paramArray, const unsigned int &size) { std::vector elements; for (size_t i = 0; i < size * size; i++) @@ -259,7 +198,7 @@ bool TIntermLoop::replaceChildNode( REPLACE_IF_IS(mInit, TIntermNode, original, replacement); REPLACE_IF_IS(mCond, TIntermTyped, original, replacement); REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement); - REPLACE_IF_IS(mBody, TIntermNode, original, replacement); + REPLACE_IF_IS(mBody, TIntermAggregate, original, replacement); return false; } @@ -320,6 +259,19 @@ bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIn return true; } +bool TIntermAggregate::areChildrenConstQualified() +{ + for (TIntermNode *&child : mSequence) + { + TIntermTyped *typed = child->getAsTyped(); + if (typed && typed->getQualifier() != EvqConst) + { + return false; + } + } + return true; +} + void TIntermAggregate::setPrecisionFromChildren() { mGotPrecisionFromChildren = true; @@ -401,12 +353,7 @@ TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node) { - size_t arraySize = mType.getObjectSize(); - mUnionArrayPointer = new TConstantUnion[arraySize]; - for (size_t i = 0u; i < arraySize; ++i) - { - mUnionArrayPointer[i] = node.mUnionArrayPointer[i]; - } + mUnionArrayPointer = node.mUnionArrayPointer; } TIntermAggregate::TIntermAggregate(const TIntermAggregate &node) @@ -587,7 +534,10 @@ void TIntermUnary::promote(const TType *funcReturnType) } } - mType.setQualifier(EvqTemporary); + if (mOperand->getQualifier() == EvqConst) + mType.setQualifier(EvqConst); + else + mType.setQualifier(EvqTemporary); } // @@ -612,10 +562,12 @@ bool TIntermBinary::promote(TInfoSink &infoSink) mLeft->getPrecision(), mRight->getPrecision()); getTypePointer()->setPrecision(higherPrecision); + TQualifier resultQualifier = EvqConst; // Binary operations results in temporary variables unless both // operands are const. if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) { + resultQualifier = EvqTemporary; getTypePointer()->setQualifier(EvqTemporary); } @@ -670,14 +622,15 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mLeft->isVector()) { mOp = EOpVectorTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, + setType(TType(basicType, higherPrecision, resultQualifier, static_cast(mRight->getCols()), 1)); } else { mOp = EOpMatrixTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, - static_cast(mRight->getCols()), static_cast(mRight->getRows()))); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mRight->getRows()))); } } else if (mLeft->isMatrix() && !mRight->isMatrix()) @@ -685,7 +638,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mRight->isVector()) { mOp = EOpMatrixTimesVector; - setType(TType(basicType, higherPrecision, EvqTemporary, + setType(TType(basicType, higherPrecision, resultQualifier, static_cast(mLeft->getRows()), 1)); } else @@ -696,8 +649,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, - static_cast(mRight->getCols()), static_cast(mLeft->getRows()))); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -708,7 +662,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isVector() || mRight->isVector()) { mOp = EOpVectorTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, + setType(TType(basicType, higherPrecision, resultQualifier, static_cast(nominalSize), 1)); } } @@ -751,8 +705,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrixAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, - static_cast(mRight->getCols()), static_cast(mLeft->getRows()))); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -765,7 +720,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (!mLeft->isVector()) return false; mOp = EOpVectorTimesScalarAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, + setType(TType(basicType, higherPrecision, resultQualifier, static_cast(mLeft->getNominalSize()), 1)); } } @@ -835,8 +790,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) { const int secondarySize = std::max( mLeft->getSecondarySize(), mRight->getSecondarySize()); - setType(TType(basicType, higherPrecision, EvqTemporary, - static_cast(nominalSize), static_cast(secondarySize))); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(nominalSize), + static_cast(secondarySize))); if (mLeft->isArray()) { ASSERT(mLeft->getArraySize() == mRight->getArraySize()); @@ -871,7 +827,14 @@ TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink) return nullptr; } TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink); - return CreateFoldedNode(constArray, this); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = EvqConst; + if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) + { + resultQualifier = EvqTemporary; + } + return CreateFoldedNode(constArray, this, resultQualifier); } TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) @@ -903,7 +866,10 @@ TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink); break; } - return CreateFoldedNode(constArray, this); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); } TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) @@ -916,8 +882,15 @@ TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) return nullptr; } } - TConstantUnion *constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); - return CreateFoldedNode(constArray, this); + TConstantUnion *constArray = nullptr; + if (isConstructor()) + constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink); + else + constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); } // @@ -928,8 +901,8 @@ TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) // TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink) { - TConstantUnion *leftArray = getUnionArrayPointer(); - TConstantUnion *rightArray = rightNode->getUnionArrayPointer(); + const TConstantUnion *leftArray = getUnionArrayPointer(); + const TConstantUnion *rightArray = rightNode->getUnionArrayPointer(); if (!leftArray) return nullptr; @@ -1229,19 +1202,12 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn { resultArray = new TConstantUnion[1]; bool equal = true; - if (getType().getBasicType() == EbtStruct) + for (size_t i = 0; i < objectSize; i++) { - equal = CompareStructure(getType(), rightArray, leftArray); - } - else - { - for (size_t i = 0; i < objectSize; i++) + if (leftArray[i] != rightArray[i]) { - if (leftArray[i] != rightArray[i]) - { - equal = false; - break; // break out of for loop - } + equal = false; + break; // break out of for loop } } if (op == EOpEqual) @@ -1276,7 +1242,7 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator // Do operations where the return type has a different number of components compared to the operand type. // - TConstantUnion *operandArray = getUnionArrayPointer(); + const TConstantUnion *operandArray = getUnionArrayPointer(); if (!operandArray) return nullptr; @@ -1489,7 +1455,7 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, // Do unary operations where the return type is the same as operand type. // - TConstantUnion *operandArray = getUnionArrayPointer(); + const TConstantUnion *operandArray = getUnionArrayPointer(); if (!operandArray) return nullptr; @@ -1935,13 +1901,113 @@ bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, F return false; } +// static +TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink) +{ + ASSERT(aggregate->getSequence()->size() > 0u); + size_t resultSize = aggregate->getType().getObjectSize(); + TConstantUnion *resultArray = new TConstantUnion[resultSize]; + TBasicType basicType = aggregate->getBasicType(); + + size_t resultIndex = 0u; + + if (aggregate->getSequence()->size() == 1u) + { + TIntermNode *argument = aggregate->getSequence()->front(); + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + // Check the special case of constructing a matrix diagonal from a single scalar, + // or a vector from a single scalar. + if (argumentConstant->getType().getObjectSize() == 1u) + { + if (aggregate->isMatrix()) + { + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) + { + for (int row = 0; row < resultRows; ++row) + { + if (col == row) + { + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; + } + } + } + else + { + while (resultIndex < resultSize) + { + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; + } + else if (aggregate->isMatrix() && argumentConstant->isMatrix()) + { + // The special case of constructing a matrix from a matrix. + int argumentCols = argumentConstant->getType().getCols(); + int argumentRows = argumentConstant->getType().getRows(); + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) + { + for (int row = 0; row < resultRows; ++row) + { + if (col < argumentCols && row < argumentRows) + { + resultArray[resultIndex].cast(basicType, + argumentUnionArray[col * argumentRows + row]); + } + else if (col == row) + { + resultArray[resultIndex].setFConst(1.0f); + } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; + } + } + + for (TIntermNode *&argument : *aggregate->getSequence()) + { + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + size_t argumentSize = argumentConstant->getType().getObjectSize(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + for (size_t i = 0u; i < argumentSize; ++i) + { + if (resultIndex >= resultSize) + break; + resultArray[resultIndex].cast(basicType, argumentUnionArray[i]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; +} + // static TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink) { TOperator op = aggregate->getOp(); TIntermSequence *sequence = aggregate->getSequence(); unsigned int paramsCount = static_cast(sequence->size()); - std::vector unionArrays(paramsCount); + std::vector unionArrays(paramsCount); std::vector objectSizes(paramsCount); size_t maxObjectSize = 0; TBasicType basicType = EbtVoid; diff --git a/gfx/angle/src/compiler/translator/IntermNode.h b/gfx/angle/src/compiler/translator/IntermNode.h index 6ea7eb6d8f6b..ad500e2b1f84 100644 --- a/gfx/angle/src/compiler/translator/IntermNode.h +++ b/gfx/angle/src/compiler/translator/IntermNode.h @@ -173,14 +173,13 @@ class TIntermLoop : public TIntermNode { public: TIntermLoop(TLoopType type, - TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr, - TIntermNode *body) - : mType(type), - mInit(init), - mCond(cond), - mExpr(expr), - mBody(body), - mUnrollFlag(false) { } + TIntermNode *init, + TIntermTyped *cond, + TIntermTyped *expr, + TIntermAggregate *body) + : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false) + { + } TIntermLoop *getAsLoopNode() override { return this; } void traverse(TIntermTraverser *it) override; @@ -190,7 +189,7 @@ class TIntermLoop : public TIntermNode TIntermNode *getInit() { return mInit; } TIntermTyped *getCondition() { return mCond; } TIntermTyped *getExpression() { return mExpr; } - TIntermNode *getBody() { return mBody; } + TIntermAggregate *getBody() { return mBody; } void setUnrollFlag(bool flag) { mUnrollFlag = flag; } bool getUnrollFlag() const { return mUnrollFlag; } @@ -200,7 +199,7 @@ class TIntermLoop : public TIntermNode TIntermNode *mInit; // for-loop initialization TIntermTyped *mCond; // loop exit condition TIntermTyped *mExpr; // for-loop expression - TIntermNode *mBody; // loop body + TIntermAggregate *mBody; // loop body bool mUnrollFlag; // Whether the loop should be unrolled or not. }; @@ -294,19 +293,25 @@ class TIntermRaw : public TIntermTyped TString mRawText; }; +// Constant folded node. +// Note that nodes may be constant folded and not be constant expressions with the EvqConst +// qualifier. This happens for example when the following expression is processed: +// "true ? 1.0 : non_constant" +// Other nodes than TIntermConstantUnion may also be constant expressions. +// class TIntermConstantUnion : public TIntermTyped { public: - TIntermConstantUnion(TConstantUnion *unionPointer, const TType &type) - : TIntermTyped(type), - mUnionArrayPointer(unionPointer) { } + TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type) + : TIntermTyped(type), mUnionArrayPointer(unionPointer) + { + } TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); } bool hasSideEffects() const override { return false; } const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } - TConstantUnion *getUnionArrayPointer() { return mUnionArrayPointer; } int getIConst(size_t index) const { @@ -325,7 +330,7 @@ class TIntermConstantUnion : public TIntermTyped return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false; } - void replaceConstantUnion(TConstantUnion *safeConstantUnion) + void replaceConstantUnion(const TConstantUnion *safeConstantUnion) { // Previous union pointer freed on pool deallocation. mUnionArrayPointer = safeConstantUnion; @@ -339,10 +344,13 @@ class TIntermConstantUnion : public TIntermTyped TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink); TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink); + static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink); static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink); protected: - TConstantUnion *mUnionArrayPointer; + // Same data may be shared between multiple constant unions, so it can't be modified. + const TConstantUnion *mUnionArrayPointer; private: typedef float(*FloatTypeUnaryFunc) (float); @@ -516,6 +524,7 @@ class TIntermAggregate : public TIntermOperator void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } + bool areChildrenConstQualified(); void setPrecisionFromChildren(); void setBuiltInFunctionPrecision(); diff --git a/gfx/angle/src/compiler/translator/Intermediate.cpp b/gfx/angle/src/compiler/translator/Intermediate.cpp index 87bf2eb16955..0adb7212b767 100644 --- a/gfx/angle/src/compiler/translator/Intermediate.cpp +++ b/gfx/angle/src/compiler/translator/Intermediate.cpp @@ -260,7 +260,7 @@ TIntermNode *TIntermediate::addSelection( // test now. // - if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0) == true) { @@ -281,22 +281,32 @@ TIntermNode *TIntermediate::addSelection( return node; } -TIntermTyped *TIntermediate::addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line) +TIntermTyped *TIntermediate::addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion) { - if (left->getType().getQualifier() == EvqConst && - right->getType().getQualifier() == EvqConst) + TQualifier resultQualifier = EvqConst; + // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression. + if (shaderVersion >= 300 || left->getQualifier() != EvqConst || + right->getQualifier() != EvqConst) { - return right; + resultQualifier = EvqTemporary; + } + + TIntermTyped *commaNode = nullptr; + if (!left->hasSideEffects()) + { + commaNode = right; } else { - TIntermTyped *commaAggregate = growAggregate(left, right, line); - commaAggregate->getAsAggregate()->setOp(EOpComma); - commaAggregate->setType(right->getType()); - commaAggregate->getTypePointer()->setQualifier(EvqTemporary); - return commaAggregate; + commaNode = growAggregate(left, right, line); + commaNode->getAsAggregate()->setOp(EOpComma); + commaNode->setType(right->getType()); } + commaNode->getTypePointer()->setQualifier(resultQualifier); + return commaNode; } // @@ -309,27 +319,33 @@ TIntermTyped *TIntermediate::addComma( TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line) { - // Right now it's safe to fold ternary operators only when all operands - // are constant. If only the condition is constant, it's theoretically - // possible to fold the ternary operator, but that requires making sure - // that the node returned from here won't be treated as a constant - // expression in case the node that gets eliminated was not a constant - // expression. - if (cond->getAsConstantUnion() && - trueBlock->getAsConstantUnion() && - falseBlock->getAsConstantUnion()) + TQualifier resultQualifier = EvqTemporary; + if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst && + falseBlock->getQualifier() == EvqConst) + { + resultQualifier = EvqConst; + } + // Note that the node resulting from here can be a constant union without being qualified as + // constant. + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0)) + { + trueBlock->getTypePointer()->setQualifier(resultQualifier); return trueBlock; + } else + { + falseBlock->getTypePointer()->setQualifier(resultQualifier); return falseBlock; + } } // // Make a selection node. // TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); - node->getTypePointer()->setQualifier(EvqTemporary); + node->getTypePointer()->setQualifier(resultQualifier); node->setLine(line); return node; @@ -359,8 +375,9 @@ TIntermCase *TIntermediate::addCase( // Returns the constant union node created. // -TIntermConstantUnion *TIntermediate::addConstantUnion( - TConstantUnion *constantUnion, const TType &type, const TSourceLoc &line) +TIntermConstantUnion *TIntermediate::addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line) { TIntermConstantUnion *node = new TIntermConstantUnion(constantUnion, type); node->setLine(line); @@ -453,33 +470,38 @@ TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate) { switch (aggregate->getOp()) { - case EOpAtan: - case EOpPow: - case EOpMod: - case EOpMin: - case EOpMax: - case EOpClamp: - case EOpMix: - case EOpStep: - case EOpSmoothStep: - case EOpMul: - case EOpOuterProduct: - case EOpLessThan: - case EOpLessThanEqual: - case EOpGreaterThan: - case EOpGreaterThanEqual: - case EOpVectorEqual: - case EOpVectorNotEqual: - case EOpDistance: - case EOpDot: - case EOpCross: - case EOpFaceForward: - case EOpReflect: - case EOpRefract: - return aggregate->fold(mInfoSink); - default: - // Constant folding not supported for the built-in. - return nullptr; + case EOpAtan: + case EOpPow: + case EOpMod: + case EOpMin: + case EOpMax: + case EOpClamp: + case EOpMix: + case EOpStep: + case EOpSmoothStep: + case EOpMul: + case EOpOuterProduct: + case EOpLessThan: + case EOpLessThanEqual: + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + case EOpDistance: + case EOpDot: + case EOpCross: + case EOpFaceForward: + case EOpReflect: + case EOpRefract: + return aggregate->fold(mInfoSink); + default: + // TODO: Add support for folding array constructors + if (aggregate->isConstructor() && !aggregate->isArray()) + { + return aggregate->fold(mInfoSink); + } + // Constant folding not supported for the built-in. + return nullptr; } return nullptr; diff --git a/gfx/angle/src/compiler/translator/Intermediate.h b/gfx/angle/src/compiler/translator/Intermediate.h index 21d0b1d6cae1..f723fc76481e 100644 --- a/gfx/angle/src/compiler/translator/Intermediate.h +++ b/gfx/angle/src/compiler/translator/Intermediate.h @@ -48,13 +48,13 @@ class TIntermediate TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line); TIntermCase *addCase( TIntermTyped *condition, const TSourceLoc &line); - TIntermTyped *addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &); - TIntermConstantUnion *addConstantUnion( - TConstantUnion *constantUnion, const TType &type, const TSourceLoc &line); - // TODO(zmo): Get rid of default value. - bool parseConstTree(const TSourceLoc &, TIntermNode *, TConstantUnion *, - TOperator, TType, bool singleConstantParam = false); + TIntermTyped *addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion); + TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line); TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *, TIntermNode *, const TSourceLoc &); TIntermBranch *addBranch(TOperator, const TSourceLoc &); diff --git a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp index 0924e9254b37..f048b050b7eb 100644 --- a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp +++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp @@ -89,6 +89,19 @@ void TOutputGLSLBase::writeBuiltInFunctionTriplet( writeTriplet(visit, preString.c_str(), ", ", ")"); } +void TOutputGLSLBase::writeLayoutQualifier(const TType &type) +{ + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) + { + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + if (layoutQualifier.location >= 0) + { + TInfoSinkBase &out = objSink(); + out << "layout(location = " << layoutQualifier.location << ") "; + } + } +} + void TOutputGLSLBase::writeVariableType(const TType &type) { TInfoSinkBase &out = objSink(); @@ -876,6 +889,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { const TIntermSequence &sequence = *(node->getSequence()); const TIntermTyped *variable = sequence.front()->getAsTyped(); + writeLayoutQualifier(variable->getType()); writeVariableType(variable->getType()); out << " "; mDeclaringVariables = true; diff --git a/gfx/angle/src/compiler/translator/OutputGLSLBase.h b/gfx/angle/src/compiler/translator/OutputGLSLBase.h index fa57c745b8a9..2ae82d15b214 100644 --- a/gfx/angle/src/compiler/translator/OutputGLSLBase.h +++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.h @@ -32,6 +32,7 @@ class TOutputGLSLBase : public TIntermTraverser protected: TInfoSinkBase &objSink() { return mObjSink; } void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr); + void writeLayoutQualifier(const TType &type); void writeVariableType(const TType &type); virtual bool writeVariablePrecision(TPrecision precision) = 0; void writeFunctionParameters(const TIntermSequence &args); diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.cpp b/gfx/angle/src/compiler/translator/OutputHLSL.cpp index 117c34a469d4..7e8302f26436 100644 --- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp +++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp @@ -35,6 +35,45 @@ bool IsSequence(TIntermNode *node) return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; } +void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) +{ + ASSERT(constUnion != nullptr); + switch (constUnion->getType()) + { + case EbtFloat: + out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); + break; + case EbtInt: + out << constUnion->getIConst(); + break; + case EbtUInt: + out << constUnion->getUConst(); + break; + case EbtBool: + out << constUnion->getBConst(); + break; + default: + UNREACHABLE(); + } +} + +const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, + const TConstantUnion *const constUnion, + const size_t size) +{ + const TConstantUnion *constUnionIterated = constUnion; + for (size_t i = 0; i < size; i++, constUnionIterated++) + { + WriteSingleConstant(out, constUnionIterated); + + if (i != size - 1) + { + out << ", "; + } + } + return constUnionIterated; +} + } // namespace namespace sh @@ -885,11 +924,12 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + + out << " x.GetDimensions(0, width, height, layers, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + out << " float2 tSized = float2(t.x * width, t.y * height);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -901,8 +941,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; @@ -924,11 +963,12 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " x.GetDimensions(0, width, height, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + out << " float2 tSized = float2(t.x * width, t.y * height);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -938,14 +978,9 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " lod += bias;\n"; } } - else if (textureFunction->method == TextureFunction::LOD) - { - out << " x.GetDimensions(0, width, height, levels);\n"; - } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; @@ -968,11 +1003,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " x.GetDimensions(0, width, height, depth, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n" + out << " float3 tSized = float3(t.x * width, t.y * height, t.z * " + "depth);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -984,8 +1021,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; @@ -1514,6 +1550,10 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) // Skip initializing the rest of the expression return false; } + else if (writeConstantInitialization(out, symbolNode, expression)) + { + return false; + } } else if (visit == InVisit) { @@ -1869,7 +1909,9 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) TIntermTyped *variable = (*sequence)[0]->getAsTyped(); ASSERT(sequence->size() == 1); - if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal)) + if (variable && + (variable->getQualifier() == EvqTemporary || + variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst)) { ensureStructDefined(variable->getType()); @@ -2911,10 +2953,13 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n } } -const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TConstantUnion *constUnion) +const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, + const TConstantUnion *const constUnion) { TInfoSinkBase &out = getInfoSink(); + const TConstantUnion *constUnionIterated = constUnion; + const TStructure* structure = type.getStruct(); if (structure) { @@ -2925,7 +2970,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC for (size_t i = 0; i < fields.size(); i++) { const TType *fieldType = fields[i]->type(); - constUnion = writeConstantUnion(*fieldType, constUnion); + constUnionIterated = writeConstantUnion(*fieldType, constUnionIterated); if (i != fields.size() - 1) { @@ -2944,31 +2989,14 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC { out << TypeString(type) << "("; } - - for (size_t i = 0; i < size; i++, constUnion++) - { - switch (constUnion->getType()) - { - case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break; - case EbtInt: out << constUnion->getIConst(); break; - case EbtUInt: out << constUnion->getUConst(); break; - case EbtBool: out << constUnion->getBConst(); break; - default: UNREACHABLE(); - } - - if (i != size - 1) - { - out << ", "; - } - } - + constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size); if (writeType) { out << ")"; } } - return constUnion; + return constUnionIterated; } void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr) @@ -2998,6 +3026,68 @@ bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *s return false; } +bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression) +{ + // We support writing constant unions and constructors that only take constant unions as + // parameters as HLSL literals. + if (expression->getAsConstantUnion()) + { + return true; + } + if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() || + !expression->getAsAggregate()->isConstructor()) + { + return false; + } + TIntermAggregate *constructor = expression->getAsAggregate(); + for (TIntermNode *&node : *constructor->getSequence()) + { + if (!node->getAsConstantUnion()) + return false; + } + return true; +} + +bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression) +{ + if (canWriteAsHLSLLiteral(expression)) + { + symbolNode->traverse(this); + if (expression->getType().isArray()) + { + out << "[" << expression->getType().getArraySize() << "]"; + } + out << " = {"; + if (expression->getAsConstantUnion()) + { + TIntermConstantUnion *nodeConst = expression->getAsConstantUnion(); + const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); + WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + } + else + { + TIntermAggregate *constructor = expression->getAsAggregate(); + ASSERT(constructor != nullptr); + for (TIntermNode *&node : *constructor->getSequence()) + { + TIntermConstantUnion *nodeConst = node->getAsConstantUnion(); + ASSERT(nodeConst); + const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); + WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + if (node != constructor->getSequence()->back()) + { + out << ", "; + } + } + } + out << "}"; + return true; + } + return false; +} + void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) { out << "#define ANGLE_USES_DEFERRED_INIT\n" diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.h b/gfx/angle/src/compiler/translator/OutputHLSL.h index ab5ddde128c9..6d321f18875c 100644 --- a/gfx/angle/src/compiler/translator/OutputHLSL.h +++ b/gfx/angle/src/compiler/translator/OutputHLSL.h @@ -47,6 +47,8 @@ class OutputHLSL : public TIntermTraverser TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); } + static bool canWriteAsHLSLLiteral(TIntermTyped *expression); + protected: void header(const BuiltInFunctionEmulator *builtInFunctionEmulator); @@ -84,6 +86,11 @@ class OutputHLSL : public TIntermTraverser // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting) bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression); + // Returns true if variable initializer could be written using literal {} notation. + bool writeConstantInitialization(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression); + void writeDeferredGlobalInitializers(TInfoSinkBase &out); void writeSelection(TIntermSelection *node); diff --git a/gfx/angle/src/compiler/translator/ParseContext.cpp b/gfx/angle/src/compiler/translator/ParseContext.cpp index 04e88daa80d6..8e4d487662ee 100644 --- a/gfx/angle/src/compiler/translator/ParseContext.cpp +++ b/gfx/angle/src/compiler/translator/ParseContext.cpp @@ -164,6 +164,23 @@ void TParseContext::warning(const TSourceLoc &loc, mDiagnostics.writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo); } +void TParseContext::outOfRangeError(bool isError, + const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo) +{ + if (isError) + { + error(loc, reason, token, extraInfo); + recover(); + } + else + { + warning(loc, reason, token, extraInfo); + } +} + // // Same error message for all places assignments don't work. // @@ -476,7 +493,7 @@ bool TParseContext::reservedErrorCheck(const TSourceLoc &line, const TString &id // Returns true if there was an error in construction. // bool TParseContext::constructorErrorCheck(const TSourceLoc &line, - TIntermNode *node, + TIntermNode *argumentsNode, TFunction &function, TOperator op, TType *type) @@ -587,21 +604,27 @@ bool TParseContext::constructorErrorCheck(const TSourceLoc &line, } } - TIntermTyped *typed = node ? node->getAsTyped() : 0; - if (typed == 0) + if (argumentsNode == nullptr) { - error(line, "constructor argument does not have a type", "constructor"); + error(line, "constructor does not have any arguments", "constructor"); return true; } - if (op != EOpConstructStruct && IsSampler(typed->getBasicType())) + + TIntermAggregate *argumentsAgg = argumentsNode->getAsAggregate(); + for (TIntermNode *&argNode : *argumentsAgg->getSequence()) { - error(line, "cannot convert a sampler", "constructor"); - return true; - } - if (typed->getBasicType() == EbtVoid) - { - error(line, "cannot convert a void", "constructor"); - return true; + TIntermTyped *argTyped = argNode->getAsTyped(); + ASSERT(argTyped != nullptr); + if (op != EOpConstructStruct && IsSampler(argTyped->getBasicType())) + { + error(line, "cannot convert a sampler", "constructor"); + return true; + } + if (argTyped->getBasicType() == EbtVoid) + { + error(line, "cannot convert a void", "constructor"); + return true; + } } return false; @@ -735,7 +758,10 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *ex { TIntermConstantUnion *constant = expr->getAsConstantUnion(); - if (constant == nullptr || !constant->isScalarInt()) + // TODO(oetuaho@nvidia.com): Get rid of the constant == nullptr check here once all constant + // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't + // fold as array size. + if (expr->getQualifier() != EvqConst || constant == nullptr || !constant->isScalarInt()) { error(line, "array size must be a constant integer expression", ""); size = 1; @@ -1182,6 +1208,24 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location, return variable; } +TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, + const TString *name, + const TSymbol *symbol) +{ + const TVariable *variable = getNamedVariable(location, name, symbol); + + if (variable->getConstPointer()) + { + const TConstantUnion *constArray = variable->getConstPointer(); + return intermediate.addConstantUnion(constArray, variable->getType(), location); + } + else + { + return intermediate.addSymbol(variable->getUniqueId(), variable->getName(), + variable->getType(), location); + } +} + // // Look up a function name in the symbol table, and make sure it is a function. // @@ -1291,9 +1335,17 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, variable->getType().setQualifier(EvqTemporary); return true; } + + // Save the constant folded value to the variable if possible. For example array + // initializers are not folded, since that way copying the array literal to multiple places + // in the shader is avoided. + // TODO(oetuaho@nvidia.com): Consider constant folding array initialization in cases where + // it would be beneficial. if (initializer->getAsConstantUnion()) { variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer()); + *intermNode = nullptr; + return false; } else if (initializer->getAsSymbolNode()) { @@ -1301,60 +1353,28 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, symbolTable.find(initializer->getAsSymbolNode()->getSymbol(), 0); const TVariable *tVar = static_cast(symbol); - TConstantUnion *constArray = tVar->getConstPointer(); - variable->shareConstPointer(constArray); - } - else - { - std::stringstream extraInfoStream; - extraInfoStream << "'" << variable->getType().getCompleteString() << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, " cannot assign to", "=", extraInfo.c_str()); - variable->getType().setQualifier(EvqTemporary); - return true; + const TConstantUnion *constArray = tVar->getConstPointer(); + if (constArray) + { + variable->shareConstPointer(constArray); + *intermNode = nullptr; + return false; + } } } - if (qualifier != EvqConst) + TIntermSymbol *intermSymbol = intermediate.addSymbol( + variable->getUniqueId(), variable->getName(), variable->getType(), line); + *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line); + if (*intermNode == nullptr) { - TIntermSymbol *intermSymbol = intermediate.addSymbol( - variable->getUniqueId(), variable->getName(), variable->getType(), line); - *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line); - if (*intermNode == nullptr) - { - assignError(line, "=", intermSymbol->getCompleteString(), - initializer->getCompleteString()); - return true; - } - } - else - { - *intermNode = nullptr; + assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + return true; } return false; } -bool TParseContext::areAllChildConst(TIntermAggregate *aggrNode) -{ - ASSERT(aggrNode != NULL); - if (!aggrNode->isConstructor()) - return false; - - bool allConstant = true; - - // check if all the child nodes are constants so that they can be inserted into - // the parent node - TIntermSequence *sequence = aggrNode->getSequence(); - for (TIntermSequence::iterator p = sequence->begin(); p != sequence->end(); ++p) - { - if (!(*p)->getAsTyped()->getAsConstantUnion()) - return false; - } - - return allConstant; -} - TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, bool invariant, TLayoutQualifier layoutQualifier, @@ -2073,6 +2093,13 @@ TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TF TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn) { TPublicType publicType = publicTypeIn; + if (publicType.isStructSpecifier) + { + error(publicType.line, "constructor can't be a structure definition", + getBasicString(publicType.type)); + recover(); + } + TOperator op = EOpNull; if (publicType.userDef) { @@ -2234,19 +2261,14 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, TFunction *fnCall, const TSourceLoc &line) { - TIntermAggregate *aggregateArguments = arguments->getAsAggregate(); - - if (!aggregateArguments) - { - aggregateArguments = new TIntermAggregate; - aggregateArguments->getSequence()->push_back(arguments); - } + TIntermAggregate *constructor = arguments->getAsAggregate(); + ASSERT(constructor != nullptr); if (type->isArray()) { // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of // the array. - TIntermSequence *args = aggregateArguments->getSequence(); + TIntermSequence *args = constructor->getSequence(); for (size_t i = 0; i < args->size(); i++) { const TType &argType = (*args)[i]->getAsTyped()->getType(); @@ -2263,7 +2285,7 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, else if (op == EOpConstructStruct) { const TFieldList &fields = type->getStruct()->fields(); - TIntermSequence *args = aggregateArguments->getSequence(); + TIntermSequence *args = constructor->getSequence(); for (size_t i = 0; i < fields.size(); i++) { @@ -2279,12 +2301,12 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, } // Turn the argument list itself into a constructor - TIntermAggregate *constructor = intermediate.setAggregateOperator(aggregateArguments, op, line); - TIntermTyped *constConstructor = foldConstConstructor(constructor, *type); - if (constConstructor) - { - return constConstructor; - } + constructor->setOp(op); + constructor->setLine(line); + ASSERT(constructor->isConstructor()); + + // Need to set type before setPrecisionFromChildren() because bool doesn't have precision. + constructor->setType(*type); // Structs should not be precision qualified, the individual members may be. // Built-in types on the other hand should be precision qualified. @@ -2294,35 +2316,13 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, type->setPrecision(constructor->getPrecision()); } - return constructor; -} - -TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, const TType &type) -{ - // TODO: Add support for folding array constructors - bool canBeFolded = areAllChildConst(aggrNode) && !type.isArray(); - aggrNode->setType(type); - if (canBeFolded) + TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor); + if (constConstructor) { - bool returnVal = false; - TConstantUnion *unionArray = new TConstantUnion[type.getObjectSize()]; - if (aggrNode->getSequence()->size() == 1) - { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, - aggrNode->getOp(), type, true); - } - else - { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, - aggrNode->getOp(), type); - } - if (returnVal) - return 0; - - return intermediate.addConstantUnion(unionArray, type, aggrNode->getLine()); + return constConstructor; } - return 0; + return constructor; } // @@ -2330,36 +2330,16 @@ TIntermTyped *TParseContext::foldConstConstructor(TIntermAggregate *aggrNode, co // vector. // If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a // contant node is returned, else an aggregate node is returned (for v.xy). The input to this -// function could either -// be the symbol node or it could be the intermediate tree representation of accessing fields in a -// constant -// structure or column of a constant matrix. +// function could either be the symbol node or it could be the intermediate tree representation of +// accessing fields in a constant structure or column of a constant matrix. // TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields, - TIntermTyped *node, - const TSourceLoc &line) + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped *typedNode; - TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); - - const TConstantUnion *unionArray; - if (tempConstantNode) - { - unionArray = tempConstantNode->getUnionArrayPointer(); - - if (!unionArray) - { - return node; - } - } - else - { // The node has to be either a symbol node or an aggregate node or a tempConstant node, else, - // its an error - error(line, "Cannot offset into the vector", "Error"); - recover(); - - return 0; - } + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + ASSERT(unionArray); TConstantUnion *constArray = new TConstantUnion[fields.num]; @@ -2370,59 +2350,39 @@ TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields, std::stringstream extraInfoStream; extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - fields.offsets[i] = 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + fields.offsets[i] = node->getType().getNominalSize() - 1; } constArray[i] = unionArray[fields.offsets[i]]; } - typedNode = intermediate.addConstantUnion(constArray, node->getType(), line); - return typedNode; + return intermediate.addConstantUnion(constArray, node->getType(), line); } // // This function returns the column being accessed from a constant matrix. The values are retrieved // from the symbol table and parse-tree is built for a vector (each column of a matrix is a vector). -// The -// input to the function could either be a symbol node (m[0] where m is a constant matrix)that -// represents -// a constant matrix or it could be the tree representation of the constant matrix (s.m1[0] where s -// is a constant structure) +// The input to the function could either be a symbol node (m[0] where m is a constant matrix)that +// represents a constant matrix or it could be the tree representation of the constant matrix +// (s.m1[0] where s is a constant structure) // TIntermTyped *TParseContext::addConstMatrixNode(int index, - TIntermTyped *node, - const TSourceLoc &line) + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped *typedNode; - TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); - if (index >= node->getType().getCols()) { std::stringstream extraInfoStream; extraInfoStream << "matrix field selection out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + index = node->getType().getCols() - 1; } - if (tempConstantNode) - { - TConstantUnion *unionArray = tempConstantNode->getUnionArrayPointer(); - int size = tempConstantNode->getType().getCols(); - typedNode = intermediate.addConstantUnion(&unionArray[size * index], - tempConstantNode->getType(), line); - } - else - { - error(line, "Cannot offset into the matrix", "Error"); - recover(); - - return 0; - } - - return typedNode; + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + int size = node->getType().getCols(); + return intermediate.addConstantUnion(&unionArray[size * index], node->getType(), line); } // @@ -2433,11 +2393,10 @@ TIntermTyped *TParseContext::addConstMatrixNode(int index, // constant structure) // TIntermTyped *TParseContext::addConstArrayNode(int index, - TIntermTyped *node, - const TSourceLoc &line) + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped *typedNode; - TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); TType arrayElementType = node->getType(); arrayElementType.clearArrayness(); @@ -2446,27 +2405,13 @@ TIntermTyped *TParseContext::addConstArrayNode(int index, std::stringstream extraInfoStream; extraInfoStream << "array field selection out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + index = node->getType().getArraySize() - 1; } - - if (tempConstantNode) - { - size_t arrayElementSize = arrayElementType.getObjectSize(); - TConstantUnion *unionArray = tempConstantNode->getUnionArrayPointer(); - typedNode = intermediate.addConstantUnion(&unionArray[arrayElementSize * index], - tempConstantNode->getType(), line); - } - else - { - error(line, "Cannot offset into the array", "Error"); - recover(); - - return 0; - } - - return typedNode; + size_t arrayElementSize = arrayElementType.getObjectSize(); + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], node->getType(), + line); } // @@ -2499,7 +2444,7 @@ TIntermTyped *TParseContext::addConstStruct(const TString &identifier, TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); if (tempConstantNode) { - TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer(); + const TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer(); // type will be changed in the calling function typedNode = intermediate.addConstantUnion(constArray + instanceSize, @@ -2760,90 +2705,11 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion(); - if (indexExpression->getQualifier() == EvqConst && indexConstantUnion) - { - int index = indexConstantUnion->getIConst(0); - if (index < 0) - { - std::stringstream infoStream; - infoStream << index; - std::string info = infoStream.str(); - error(location, "negative index", info.c_str()); - recover(); - index = 0; - } - if (baseExpression->getType().getQualifier() == EvqConst) - { - if (baseExpression->isArray()) - { - // constant folding for arrays - indexedExpression = addConstArrayNode(index, baseExpression, location); - } - else if (baseExpression->isVector()) - { - // constant folding for vectors - TVectorFields fields; - fields.num = 1; - fields.offsets[0] = - index; // need to do it this way because v.xy sends fields integer array - indexedExpression = addConstVectorNode(fields, baseExpression, location); - } - else if (baseExpression->isMatrix()) - { - // constant folding for matrices - indexedExpression = addConstMatrixNode(index, baseExpression, location); - } - } - else - { - int safeIndex = -1; - - if (baseExpression->isArray()) - { - if (index >= baseExpression->getType().getArraySize()) - { - std::stringstream extraInfoStream; - extraInfoStream << "array index out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - safeIndex = baseExpression->getType().getArraySize() - 1; - } - else if (baseExpression->getQualifier() == EvqFragData && index > 0 && - !isExtensionEnabled("GL_EXT_draw_buffers")) - { - error(location, "", "[", - "array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is " - "disabled"); - recover(); - safeIndex = 0; - } - } - else if ((baseExpression->isVector() || baseExpression->isMatrix()) && - baseExpression->getType().getNominalSize() <= index) - { - std::stringstream extraInfoStream; - extraInfoStream << "field selection out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - safeIndex = baseExpression->getType().getNominalSize() - 1; - } - - // Don't modify the data of the previous constant union, because it can point - // to builtins, like gl_MaxDrawBuffers. Instead use a new sanitized object. - if (safeIndex != -1) - { - TConstantUnion *safeConstantUnion = new TConstantUnion(); - safeConstantUnion->setIConst(safeIndex); - indexConstantUnion->replaceConstantUnion(safeConstantUnion); - } - - indexedExpression = - intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); - } - } - else + // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able + // to constant fold all constant expressions. Right now we don't allow indexing interface blocks + // or fragment outputs with expressions that ANGLE is not able to constant fold, even if the + // index is a constant expression. + if (indexExpression->getQualifier() != EvqConst || indexConstantUnion == nullptr) { if (baseExpression->isInterfaceBlock()) { @@ -2858,7 +2724,119 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, "array indexes for fragment outputs must be constant integral expressions"); recover(); } + else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData) + { + error(location, "", "[", "array index for gl_FragData must be constant zero"); + recover(); + } + } + if (indexConstantUnion) + { + // If the index is not qualified as constant, the behavior in the spec is undefined. This + // applies even if ANGLE has been able to constant fold it (ANGLE may constant fold + // expressions that are not constant expressions). The most compatible way to handle this + // case is to report a warning instead of an error and force the index to be in the + // correct range. + bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst; + int index = indexConstantUnion->getIConst(0); + if (index < 0) + { + std::stringstream infoStream; + infoStream << index; + std::string info = infoStream.str(); + outOfRangeError(outOfRangeIndexIsError, location, "negative index", info.c_str()); + index = 0; + } + TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion(); + if (baseConstantUnion) + { + if (baseExpression->isArray()) + { + // constant folding for array indexing + indexedExpression = + addConstArrayNode(index, baseConstantUnion, location, outOfRangeIndexIsError); + } + else if (baseExpression->isVector()) + { + // constant folding for vector indexing + TVectorFields fields; + fields.num = 1; + fields.offsets[0] = + index; // need to do it this way because v.xy sends fields integer array + indexedExpression = + addConstVectorNode(fields, baseConstantUnion, location, outOfRangeIndexIsError); + } + else if (baseExpression->isMatrix()) + { + // constant folding for matrix indexing + indexedExpression = + addConstMatrixNode(index, baseConstantUnion, location, outOfRangeIndexIsError); + } + } + else + { + int safeIndex = -1; + + if (baseExpression->isArray()) + { + if (baseExpression->getQualifier() == EvqFragData && index > 0) + { + if (mShaderSpec == SH_WEBGL2_SPEC) + { + // Error has been already generated if index is not const. + if (indexExpression->getQualifier() == EvqConst) + { + error(location, "", "[", + "array index for gl_FragData must be constant zero"); + recover(); + } + safeIndex = 0; + } + else if (!isExtensionEnabled("GL_EXT_draw_buffers")) + { + outOfRangeError(outOfRangeIndexIsError, location, "", "[", + "array index for gl_FragData must be zero when " + "GL_EXT_draw_buffers is disabled"); + safeIndex = 0; + } + } + // Only do generic out-of-range check if similar error hasn't already been reported. + if (safeIndex < 0 && index >= baseExpression->getType().getArraySize()) + { + std::stringstream extraInfoStream; + extraInfoStream << "array index out of range '" << index << "'"; + std::string extraInfo = extraInfoStream.str(); + outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str()); + safeIndex = baseExpression->getType().getArraySize() - 1; + } + } + else if ((baseExpression->isVector() || baseExpression->isMatrix()) && + baseExpression->getType().getNominalSize() <= index) + { + std::stringstream extraInfoStream; + extraInfoStream << "field selection out of range '" << index << "'"; + std::string extraInfo = extraInfoStream.str(); + outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str()); + safeIndex = baseExpression->getType().getNominalSize() - 1; + } + + // Data of constant unions can't be changed, because it may be shared with other + // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new + // sanitized object. + if (safeIndex != -1) + { + TConstantUnion *safeConstantUnion = new TConstantUnion(); + safeConstantUnion->setIConst(safeIndex); + indexConstantUnion->replaceConstantUnion(safeConstantUnion); + } + + indexedExpression = + intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); + } + } + else + { indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location); } @@ -2929,31 +2907,29 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre recover(); } - if (baseExpression->getType().getQualifier() == EvqConst) + if (baseExpression->getAsConstantUnion()) { // constant folding for vector fields - indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation); - if (indexedExpression == 0) - { - recover(); - indexedExpression = baseExpression; - } - else - { - indexedExpression->setType(TType(baseExpression->getBasicType(), - baseExpression->getPrecision(), EvqConst, - (unsigned char)(fieldString).size())); - } + indexedExpression = addConstVectorNode(fields, baseExpression->getAsConstantUnion(), + fieldLocation, true); } else { - TString vectorString = fieldString; TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation); indexedExpression = intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation); + } + if (indexedExpression == nullptr) + { + recover(); + indexedExpression = baseExpression; + } + else + { + // Note that the qualifier set here will be corrected later. indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary, - (unsigned char)vectorString.size())); + (unsigned char)(fieldString).size())); } } else if (baseExpression->getBasicType() == EbtStruct) @@ -2979,7 +2955,7 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre } if (fieldFound) { - if (baseExpression->getType().getQualifier() == EvqConst) + if (baseExpression->getAsConstantUnion()) { indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation); if (indexedExpression == 0) @@ -2990,9 +2966,6 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre else { indexedExpression->setType(*fields[i]->type()); - // change the qualifier of the return type, not of the structure field - // as the structure definition is shared between various structures. - indexedExpression->getTypePointer()->setQualifier(EvqConst); } } else @@ -3071,6 +3044,15 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre indexedExpression = baseExpression; } + if (baseExpression->getQualifier() == EvqConst) + { + indexedExpression->getTypePointer()->setQualifier(EvqConst); + } + else + { + indexedExpression->getTypePointer()->setQualifier(EvqTemporary); + } + return indexedExpression; } @@ -3324,6 +3306,7 @@ TPublicType TParseContext::addStructure(const TSourceLoc &structLine, TPublicType publicType; publicType.setBasic(EbtStruct, EvqTemporary, structLine); publicType.userDef = structureType; + publicType.isStructSpecifier = true; exitStructDeclaration(); return publicType; @@ -3383,7 +3366,10 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l recover(); } TIntermConstantUnion *conditionConst = condition->getAsConstantUnion(); - if (conditionConst == nullptr) + // TODO(oetuaho@nvidia.com): Get rid of the conditionConst == nullptr check once all constant + // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't + // fold in case labels. + if (condition->getQualifier() != EvqConst || conditionConst == nullptr) { error(condition->getLine(), "case label must be constant", "case"); recover(); @@ -3723,6 +3709,13 @@ TIntermTyped *TParseContext::addAssign(TOperator op, return node; } +TIntermTyped *TParseContext::addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) +{ + return intermediate.addComma(left, right, loc, mShaderVersion); +} + TIntermBranch *TParseContext::addBranch(TOperator op, const TSourceLoc &loc) { switch (op) @@ -3774,6 +3767,59 @@ TIntermBranch *TParseContext::addBranch(TOperator op, return intermediate.addBranch(op, returnValue, loc); } +void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall) +{ + ASSERT(!functionCall->isUserDefined()); + const TString &name = functionCall->getName(); + TIntermNode *offset = nullptr; + TIntermSequence *arguments = functionCall->getSequence(); + if (name.compare(0, 16, "texelFetchOffset") == 0 || + name.compare(0, 16, "textureLodOffset") == 0 || + name.compare(0, 20, "textureProjLodOffset") == 0 || + name.compare(0, 17, "textureGradOffset") == 0 || + name.compare(0, 21, "textureProjGradOffset") == 0) + { + offset = arguments->back(); + } + else if (name.compare(0, 13, "textureOffset") == 0 || + name.compare(0, 17, "textureProjOffset") == 0) + { + // A bias parameter might follow the offset parameter. + ASSERT(arguments->size() >= 3); + offset = (*arguments)[2]; + } + if (offset != nullptr) + { + TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion(); + if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion) + { + TString unmangledName = TFunction::unmangleName(name); + error(functionCall->getLine(), "Texture offset must be a constant expression", + unmangledName.c_str()); + recover(); + } + else + { + ASSERT(offsetConstantUnion->getBasicType() == EbtInt); + size_t size = offsetConstantUnion->getType().getObjectSize(); + const TConstantUnion *values = offsetConstantUnion->getUnionArrayPointer(); + for (size_t i = 0u; i < size; ++i) + { + int offsetValue = values[i].getIConst(); + if (offsetValue > mMaxProgramTexelOffset || offsetValue < mMinProgramTexelOffset) + { + std::stringstream tokenStream; + tokenStream << offsetValue; + std::string token = tokenStream.str(); + error(offset->getLine(), "Texture offset value out of valid range", + token.c_str()); + recover(); + } + } + } + } +} + TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *paramNode, TIntermNode *thisNode, @@ -3879,6 +3925,8 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, // // Treat it like a built-in unary operator. // + TIntermAggregate *paramAgg = paramNode->getAsAggregate(); + paramNode = paramAgg->getSequence()->front(); callNode = createUnaryMath(op, paramNode->getAsTyped(), loc, &fnCandidate->getReturnType()); if (callNode == nullptr) @@ -3900,11 +3948,16 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, intermediate.setAggregateOperator(paramNode, op, loc); aggregate->setType(fnCandidate->getReturnType()); aggregate->setPrecisionFromChildren(); + if (aggregate->areChildrenConstQualified()) + { + aggregate->getTypePointer()->setQualifier(EvqConst); + } // Some built-in functions have out parameters too. functionCallLValueErrorCheck(fnCandidate, aggregate); - // See if we can constant fold a built-in. + // See if we can constant fold a built-in. Note that this may be possible even + // if it is not const-qualified. TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate); if (foldedNode) { @@ -3919,7 +3972,6 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, else { // This is a real function call - TIntermAggregate *aggregate = intermediate.setAggregateOperator(paramNode, EOpFunctionCall, loc); aggregate->setType(fnCandidate->getReturnType()); @@ -3936,8 +3988,12 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, // This needs to happen after the name is set if (builtIn) + { aggregate->setBuiltInFunctionPrecision(); + checkTextureOffsetConst(aggregate); + } + callNode = aggregate; functionCallLValueErrorCheck(fnCandidate, aggregate); diff --git a/gfx/angle/src/compiler/translator/ParseContext.h b/gfx/angle/src/compiler/translator/ParseContext.h index 35d2d3dd7cf5..da7c42047ac6 100644 --- a/gfx/angle/src/compiler/translator/ParseContext.h +++ b/gfx/angle/src/compiler/translator/ParseContext.h @@ -36,7 +36,7 @@ class TParseContext : angle::NonCopyable int options, bool checksPrecErrors, TInfoSink &is, - bool debugShaderPrecisionSupported) + const ShBuiltInResources &resources) : intermediate(interm), symbolTable(symt), mDeferredSingleDeclarationErrorCheck(false), @@ -50,16 +50,21 @@ class TParseContext : angle::NonCopyable mCurrentFunctionType(nullptr), mFunctionReturnsValue(false), mChecksPrecisionErrors(checksPrecErrors), - mFragmentPrecisionHigh(false), + mFragmentPrecisionHighOnESSL1(false), mDefaultMatrixPacking(EmpColumnMajor), mDefaultBlockStorage(EbsShared), mDiagnostics(is), - mDirectiveHandler(ext, mDiagnostics, mShaderVersion, debugShaderPrecisionSupported), + mDirectiveHandler(ext, + mDiagnostics, + mShaderVersion, + resources.WEBGL_debug_shader_precision == 1), mPreprocessor(&mDiagnostics, &mDirectiveHandler), mScanner(nullptr), mUsesFragData(false), mUsesFragColor(false), - mUsesSecondaryOutputs(false) + mUsesSecondaryOutputs(false), + mMinProgramTexelOffset(resources.MinProgramTexelOffset), + mMaxProgramTexelOffset(resources.MaxProgramTexelOffset) { } @@ -77,14 +82,24 @@ class TParseContext : angle::NonCopyable void warning(const TSourceLoc &loc, const char *reason, const char *token, const char *extraInfo=""); + // If isError is false, a warning will be reported instead. + void outOfRangeError(bool isError, + const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo = ""); + void recover(); TIntermNode *getTreeRoot() const { return mTreeRoot; } void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; } - bool getFragmentPrecisionHigh() const { return mFragmentPrecisionHigh; } - void setFragmentPrecisionHigh(bool fragmentPrecisionHigh) + bool getFragmentPrecisionHigh() const { - mFragmentPrecisionHigh = fragmentPrecisionHigh; + return mFragmentPrecisionHighOnESSL1 || mShaderVersion >= 300; + } + void setFragmentPrecisionHighOnESSL1(bool fragmentPrecisionHigh) + { + mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh; } bool getFunctionReturnsValue() const { return mFunctionReturnsValue; } @@ -112,6 +127,9 @@ class TParseContext : angle::NonCopyable // This method is guaranteed to succeed, even if no variable with 'name' exists. const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol); + TIntermTyped *parseVariableIdentifier(const TSourceLoc &location, + const TString *name, + const TSymbol *symbol); bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line); @@ -124,7 +142,11 @@ class TParseContext : angle::NonCopyable bool constErrorCheck(TIntermTyped *node); bool integerErrorCheck(TIntermTyped *node, const char *token); bool globalErrorCheck(const TSourceLoc &line, bool global, const char *token); - bool constructorErrorCheck(const TSourceLoc &line, TIntermNode*, TFunction&, TOperator, TType*); + bool constructorErrorCheck(const TSourceLoc &line, + TIntermNode *argumentsNode, + TFunction &function, + TOperator op, + TType *type); bool arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size); bool arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type); bool arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type); @@ -152,7 +174,6 @@ class TParseContext : angle::NonCopyable void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl); bool containsSampler(const TType &type); - bool areAllChildConst(TIntermAggregate *aggrNode); const TFunction* findFunction( const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0); bool executeInitializer(const TSourceLoc &line, @@ -234,10 +255,18 @@ class TParseContext : angle::NonCopyable TOperator op, TFunction *fnCall, const TSourceLoc &line); - TIntermTyped *foldConstConstructor(TIntermAggregate *aggrNode, const TType &type); - TIntermTyped *addConstVectorNode(TVectorFields&, TIntermTyped*, const TSourceLoc&); - TIntermTyped *addConstMatrixNode(int, TIntermTyped*, const TSourceLoc&); - TIntermTyped *addConstArrayNode(int index, TIntermTyped *node, const TSourceLoc &line); + TIntermTyped *addConstVectorNode(TVectorFields &fields, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); + TIntermTyped *addConstMatrixNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); + TIntermTyped *addConstArrayNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); TIntermTyped *addConstStruct( const TString &identifier, TIntermTyped *node, const TSourceLoc& line); TIntermTyped *addIndexExpression(TIntermTyped *baseExpression, @@ -295,9 +324,12 @@ class TParseContext : angle::NonCopyable TIntermTyped *addAssign( TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc); + void checkTextureOffsetConst(TIntermAggregate *functionCall); TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *paramNode, TIntermNode *thisNode, @@ -342,7 +374,8 @@ class TParseContext : angle::NonCopyable const TType *mCurrentFunctionType; // the return type of the function that's currently being parsed bool mFunctionReturnsValue; // true if a non-void function has a return bool mChecksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit. - bool mFragmentPrecisionHigh; // true if highp precision is supported in the fragment language. + bool mFragmentPrecisionHighOnESSL1; // true if highp precision is supported when compiling + // ESSL1. TLayoutMatrixPacking mDefaultMatrixPacking; TLayoutBlockStorage mDefaultBlockStorage; TString mHashErrMsg; @@ -354,6 +387,8 @@ class TParseContext : angle::NonCopyable bool mUsesFragColor; bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or // gl_Secondary FragColor or both. + int mMinProgramTexelOffset; + int mMaxProgramTexelOffset; }; int PaParseStrings( diff --git a/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp index b08627139651..74814f22a793 100644 --- a/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp +++ b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp @@ -55,10 +55,11 @@ TName GetIndexFunctionName(const TType &type, bool write) return name; } -TIntermSymbol *CreateBaseSymbol(const TType &type) +TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier) { TIntermSymbol *symbol = new TIntermSymbol(0, "base", type); symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(qualifier); return symbol; } @@ -66,6 +67,7 @@ TIntermSymbol *CreateIndexSymbol() { TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh)); symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(EvqIn); return symbol; } @@ -73,6 +75,7 @@ TIntermSymbol *CreateValueSymbol(const TType &type) { TIntermSymbol *symbol = new TIntermSymbol(0, "value", type); symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(EvqIn); return symbol; } @@ -85,11 +88,13 @@ TIntermConstantUnion *CreateIntConstantNode(int i) TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType, const TType &fieldType, - const int index) + const int index, + TQualifier baseQualifier) { TIntermBinary *indexNode = new TIntermBinary(EOpIndexDirect); indexNode->setType(fieldType); - indexNode->setLeft(CreateBaseSymbol(indexedType)); + TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier); + indexNode->setLeft(baseSymbol); indexNode->setRight(CreateIntConstantNode(index)); return indexNode; } @@ -203,19 +208,16 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write) } TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters); - TIntermSymbol *baseParam = CreateBaseSymbol(type); - if (write) - baseParam->getTypePointer()->setQualifier(EvqInOut); - else - baseParam->getTypePointer()->setQualifier(EvqIn); + TQualifier baseQualifier = EvqInOut; + if (!write) + baseQualifier = EvqIn; + TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier); paramsNode->getSequence()->push_back(baseParam); TIntermSymbol *indexParam = CreateIndexSymbol(); - indexParam->getTypePointer()->setQualifier(EvqIn); paramsNode->getSequence()->push_back(indexParam); if (write) { TIntermSymbol *valueParam = CreateValueSymbol(fieldType); - valueParam->getTypePointer()->setQualifier(EvqIn); paramsNode->getSequence()->push_back(valueParam); } indexingFunction->getSequence()->push_back(paramsNode); @@ -226,7 +228,8 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write) TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i)); statementList->getSequence()->push_back(caseNode); - TIntermBinary *indexNode = CreateIndexDirectBaseSymbolNode(type, fieldType, i); + TIntermBinary *indexNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier); if (write) { TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType); @@ -260,9 +263,11 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write) // Two blocks: one accesses (either reads or writes) the first element and returns, // the other accesses the last element. TIntermAggregate *useFirstBlock = new TIntermAggregate(EOpSequence); - TIntermAggregate *useLastBlock = new TIntermAggregate(EOpSequence); - TIntermBinary *indexFirstNode = CreateIndexDirectBaseSymbolNode(type, fieldType, 0); - TIntermBinary *indexLastNode = CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1); + TIntermAggregate *useLastBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *indexFirstNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier); + TIntermBinary *indexLastNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier); if (write) { TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType); diff --git a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp index f8d14660a43c..de9050cd80a0 100644 --- a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp +++ b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp @@ -9,10 +9,16 @@ // will effectively become // type[n] a; // a = initializer; +// +// Note that if the array is declared as const, the initialization may still be split, making the +// AST technically invalid. Because of that this transformation should only be used when subsequent +// stages don't care about const qualifiers. However, the initialization will not be split if the +// initializer can be written as a HLSL literal. #include "compiler/translator/SeparateArrayInitialization.h" #include "compiler/translator/IntermNode.h" +#include "compiler/translator/OutputHLSL.h" namespace { @@ -47,7 +53,7 @@ bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node) if (initNode != nullptr && initNode->getOp() == EOpInitialize) { TIntermTyped *initializer = initNode->getRight(); - if (initializer->isArray()) + if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer)) { // We rely on that array declarations have been isolated to single declarations. ASSERT(sequence->size() == 1); diff --git a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h index d7c3ae609bd3..d16357a3afd8 100644 --- a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h +++ b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h @@ -9,6 +9,11 @@ // will effectively become // type[n] a; // a = initializer; +// +// Note that if the array is declared as const, the initialization may still be split, making the +// AST technically invalid. Because of that this transformation should only be used when subsequent +// stages don't care about const qualifiers. However, the initialization will not be split if the +// initializer can be written as a HLSL literal. #ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ #define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ diff --git a/gfx/angle/src/compiler/translator/SymbolTable.h b/gfx/angle/src/compiler/translator/SymbolTable.h index 66b9174cb9ba..2f5eb369f8ae 100644 --- a/gfx/angle/src/compiler/translator/SymbolTable.h +++ b/gfx/angle/src/compiler/translator/SymbolTable.h @@ -128,34 +128,16 @@ class TVariable : public TSymbol type.setQualifier(qualifier); } - TConstantUnion *getConstPointer() - { - if (!unionArray) - unionArray = new TConstantUnion[type.getObjectSize()]; + const TConstantUnion *getConstPointer() const { return unionArray; } - return unionArray; - } - - TConstantUnion *getConstPointer() const - { - return unionArray; - } - - void shareConstPointer(TConstantUnion *constArray) - { - if (unionArray == constArray) - return; - - delete[] unionArray; - unionArray = constArray; - } + void shareConstPointer(const TConstantUnion *constArray) { unionArray = constArray; } private: TType type; bool userType; // we are assuming that Pool Allocator will free the memory // allocated to unionArray when this object is destroyed. - TConstantUnion *unionArray; + const TConstantUnion *unionArray; }; // Immutable version of TParameter. @@ -400,7 +382,9 @@ class TSymbolTable : angle::NonCopyable { TVariable *constant = new TVariable( NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); - constant->getConstPointer()->setIConst(value); + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray[0].setIConst(value); + constant->shareConstPointer(unionArray); return insert(level, constant); } @@ -408,7 +392,9 @@ class TSymbolTable : angle::NonCopyable { TVariable *constant = new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); - constant->getConstPointer()->setIConst(value); + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray[0].setIConst(value); + constant->shareConstPointer(unionArray); return insert(level, ext, constant); } diff --git a/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp b/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp index e62b7735dff3..05e9eb310bad 100644 --- a/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp +++ b/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp @@ -9,6 +9,7 @@ #include "angle_gl.h" #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" #include "compiler/translator/EmulatePrecision.h" +#include "compiler/translator/ExtensionGLSL.h" #include "compiler/translator/OutputGLSL.h" #include "compiler/translator/VersionGLSL.h" @@ -39,7 +40,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions) writePragma(); // Write extension behaviour as needed - writeExtensionBehavior(); + writeExtensionBehavior(root); bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision; @@ -156,19 +157,41 @@ void TranslatorGLSL::writeVersion(TIntermNode *root) } } -void TranslatorGLSL::writeExtensionBehavior() { +void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root) +{ TInfoSinkBase& sink = getInfoSink().obj; const TExtensionBehavior& extBehavior = getExtensionBehavior(); - for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); - iter != extBehavior.end(); ++iter) { - if (iter->second == EBhUndefined) + for (const auto &iter : extBehavior) + { + if (iter.second == EBhUndefined) + { continue; + } // For GLSL output, we don't need to emit most extensions explicitly, // but some we need to translate. - if (iter->first == "GL_EXT_shader_texture_lod") { - sink << "#extension GL_ARB_shader_texture_lod : " - << getBehaviorString(iter->second) << "\n"; + if (iter.first == "GL_EXT_shader_texture_lod") + { + sink << "#extension GL_ARB_shader_texture_lod : " << getBehaviorString(iter.second) + << "\n"; } } + + // GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330 + if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT) + { + sink << "#extension GL_ARB_explicit_attrib_location : require\n"; + } + + TExtensionGLSL extensionGLSL(getOutputType()); + root->traverse(&extensionGLSL); + + for (const auto &ext : extensionGLSL.getEnabledExtensions()) + { + sink << "#extension " << ext << " : enable\n"; + } + for (const auto &ext : extensionGLSL.getRequiredExtensions()) + { + sink << "#extension " << ext << " : require\n"; + } } diff --git a/gfx/angle/src/compiler/translator/TranslatorGLSL.h b/gfx/angle/src/compiler/translator/TranslatorGLSL.h index 3eeb4d1129b7..4f07b2198006 100644 --- a/gfx/angle/src/compiler/translator/TranslatorGLSL.h +++ b/gfx/angle/src/compiler/translator/TranslatorGLSL.h @@ -21,7 +21,7 @@ class TranslatorGLSL : public TCompiler private: void writeVersion(TIntermNode *root); - void writeExtensionBehavior(); + void writeExtensionBehavior(TIntermNode *root); }; #endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_ diff --git a/gfx/angle/src/compiler/translator/Types.h b/gfx/angle/src/compiler/translator/Types.h index d3c0e6f4f807..c2968dcebaab 100644 --- a/gfx/angle/src/compiler/translator/Types.h +++ b/gfx/angle/src/compiler/translator/Types.h @@ -582,6 +582,9 @@ struct TPublicType TType *userDef; TSourceLoc line; + // true if the type was defined by a struct specifier rather than a reference to a type name. + bool isStructSpecifier; + void setBasic(TBasicType bt, TQualifier q, const TSourceLoc &ln) { type = bt; @@ -595,6 +598,7 @@ struct TPublicType arraySize = 0; userDef = 0; line = ln; + isStructSpecifier = false; } void setAggregate(unsigned char size) diff --git a/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp index 33b787a26564..be23b524d757 100644 --- a/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp +++ b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp @@ -24,19 +24,39 @@ class UnfoldShortCircuitTraverser : public TIntermTraverser bool visitBinary(Visit visit, TIntermBinary *node) override; bool visitAggregate(Visit visit, TIntermAggregate *node) override; bool visitSelection(Visit visit, TIntermSelection *node) override; + bool visitLoop(Visit visit, TIntermLoop *node) override; void nextIteration(); bool foundShortCircuit() const { return mFoundShortCircuit; } protected: + // Check if the traversal is inside a loop condition or expression, in which case the unfolded + // expression needs to be copied inside the loop. Returns true if the copying is done, in which + // case no further unfolding should be done on the same traversal. + // The parameters are the node that will be unfolded to multiple statements and so can't remain + // inside a loop condition, and its parent. + bool copyLoopConditionOrExpression(TIntermNode *parent, TIntermTyped *node); + // Marked to true once an operation that needs to be unfolded has been found. // After that, no more unfolding is performed on that traversal. bool mFoundShortCircuit; + + // Set to the loop node while a loop condition or expression is being traversed. + TIntermLoop *mParentLoop; + // Parent of the loop node while a loop condition or expression is being traversed. + TIntermNode *mLoopParent; + + bool mInLoopCondition; + bool mInLoopExpression; }; UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser() : TIntermTraverser(true, false, true), - mFoundShortCircuit(false) + mFoundShortCircuit(false), + mParentLoop(nullptr), + mLoopParent(nullptr), + mInLoopCondition(false), + mInLoopExpression(false) { } @@ -56,10 +76,12 @@ bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node) { case EOpLogicalOr: mFoundShortCircuit = true; - - // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; else s = y;", - // and then further simplifies down to "bool s = x; if(!s) s = y;". + if (!copyLoopConditionOrExpression(getParentNode(), node)) { + // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; + // else s = y;", + // and then further simplifies down to "bool s = x; if(!s) s = y;". + TIntermSequence insertions; TType boolType(EbtBool, EbpUndefined, EvqTemporary); @@ -83,10 +105,11 @@ bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node) return false; case EOpLogicalAnd: mFoundShortCircuit = true; - - // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; else s = false;", - // and then further simplifies down to "bool s = x; if(s) s = y;". + if (!copyLoopConditionOrExpression(getParentNode(), node)) { + // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; + // else s = false;", + // and then further simplifies down to "bool s = x; if(s) s = y;". TIntermSequence insertions; TType boolType(EbtBool, EbpUndefined, EvqTemporary); @@ -120,29 +143,35 @@ bool UnfoldShortCircuitTraverser::visitSelection(Visit visit, TIntermSelection * if (visit == PreVisit && node->usesTernaryOperator()) { mFoundShortCircuit = true; - TIntermSequence insertions; + if (!copyLoopConditionOrExpression(getParentNode(), node)) + { + TIntermSequence insertions; - TIntermSymbol *tempSymbol = createTempSymbol(node->getType()); - TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); - tempDeclaration->getSequence()->push_back(tempSymbol); - insertions.push_back(tempDeclaration); + TIntermSymbol *tempSymbol = createTempSymbol(node->getType()); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(tempSymbol); + insertions.push_back(tempDeclaration); - TIntermAggregate *trueBlock = new TIntermAggregate(EOpSequence); - TIntermBinary *trueAssignment = createTempAssignment(node->getTrueBlock()->getAsTyped()); - trueBlock->getSequence()->push_back(trueAssignment); + TIntermAggregate *trueBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *trueAssignment = + createTempAssignment(node->getTrueBlock()->getAsTyped()); + trueBlock->getSequence()->push_back(trueAssignment); - TIntermAggregate *falseBlock = new TIntermAggregate(EOpSequence); - TIntermBinary *falseAssignment = createTempAssignment(node->getFalseBlock()->getAsTyped()); - falseBlock->getSequence()->push_back(falseAssignment); + TIntermAggregate *falseBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *falseAssignment = + createTempAssignment(node->getFalseBlock()->getAsTyped()); + falseBlock->getSequence()->push_back(falseAssignment); - TIntermSelection *ifNode = new TIntermSelection(node->getCondition()->getAsTyped(), trueBlock, falseBlock); - insertions.push_back(ifNode); + TIntermSelection *ifNode = + new TIntermSelection(node->getCondition()->getAsTyped(), trueBlock, falseBlock); + insertions.push_back(ifNode); - insertStatementsInParentBlock(insertions); + insertStatementsInParentBlock(insertions); - TIntermSymbol *ternaryResult = createTempSymbol(node->getType()); - NodeUpdateEntry replaceVariable(getParentNode(), node, ternaryResult, false); - mReplacements.push_back(replaceVariable); + TIntermSymbol *ternaryResult = createTempSymbol(node->getType()); + NodeUpdateEntry replaceVariable(getParentNode(), node, ternaryResult, false); + mReplacements.push_back(replaceVariable); + } return false; } @@ -170,27 +199,150 @@ bool UnfoldShortCircuitTraverser::visitAggregate(Visit visit, TIntermAggregate * mMultiReplacements.clear(); mInsertions.clear(); - TIntermSequence insertions; - TIntermSequence *seq = node->getSequence(); - - TIntermSequence::size_type i = 0; - ASSERT(!seq->empty()); - while (i < seq->size() - 1) + if (!copyLoopConditionOrExpression(getParentNode(), node)) { - TIntermTyped *child = (*seq)[i]->getAsTyped(); - insertions.push_back(child); - ++i; + TIntermSequence insertions; + TIntermSequence *seq = node->getSequence(); + + TIntermSequence::size_type i = 0; + ASSERT(!seq->empty()); + while (i < seq->size() - 1) + { + TIntermTyped *child = (*seq)[i]->getAsTyped(); + insertions.push_back(child); + ++i; + } + + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, (*seq)[i], false); + mReplacements.push_back(replaceVariable); } - - insertStatementsInParentBlock(insertions); - - NodeUpdateEntry replaceVariable(getParentNode(), node, (*seq)[i], false); - mReplacements.push_back(replaceVariable); } } return true; } +bool UnfoldShortCircuitTraverser::visitLoop(Visit visit, TIntermLoop *node) +{ + if (visit == PreVisit) + { + if (mFoundShortCircuit) + return false; // No need to traverse further + + mLoopParent = getParentNode(); + mParentLoop = node; + incrementDepth(node); + + if (node->getInit()) + { + node->getInit()->traverse(this); + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getCondition()) + { + mInLoopCondition = true; + node->getCondition()->traverse(this); + mInLoopCondition = false; + + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getExpression()) + { + mInLoopExpression = true; + node->getExpression()->traverse(this); + mInLoopExpression = false; + + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getBody()) + node->getBody()->traverse(this); + + decrementDepth(); + } + return false; +} + +bool UnfoldShortCircuitTraverser::copyLoopConditionOrExpression(TIntermNode *parent, + TIntermTyped *node) +{ + if (mInLoopCondition) + { + mReplacements.push_back( + NodeUpdateEntry(parent, node, createTempSymbol(node->getType()), false)); + TIntermAggregate *body = mParentLoop->getBody(); + TIntermSequence empty; + if (mParentLoop->getType() == ELoopDoWhile) + { + // Declare the temporary variable before the loop. + TIntermSequence insertionsBeforeLoop; + insertionsBeforeLoop.push_back(createTempDeclaration(node->getType())); + insertStatementsInParentBlock(insertionsBeforeLoop); + + // Move a part of do-while loop condition to inside the loop. + TIntermSequence insertionsInLoop; + insertionsInLoop.push_back(createTempAssignment(node)); + mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, + empty, insertionsInLoop)); + } + else + { + // The loop initializer expression and one copy of the part of the loop condition are + // executed before the loop. They need to be in a new scope. + TIntermAggregate *loopScope = new TIntermAggregate(EOpSequence); + + TIntermNode *initializer = mParentLoop->getInit(); + if (initializer != nullptr) + { + // Move the initializer to the newly created outer scope, so that condition can + // depend on it. + mReplacements.push_back(NodeUpdateEntry(mParentLoop, initializer, nullptr, false)); + loopScope->getSequence()->push_back(initializer); + } + + loopScope->getSequence()->push_back(createTempInitDeclaration(node)); + loopScope->getSequence()->push_back(mParentLoop); + mReplacements.push_back(NodeUpdateEntry(mLoopParent, mParentLoop, loopScope, true)); + + // The second copy of the part of the loop condition is executed inside the loop. + TIntermSequence insertionsInLoop; + insertionsInLoop.push_back(createTempAssignment(node->deepCopy())); + mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, + empty, insertionsInLoop)); + } + return true; + } + + if (mInLoopExpression) + { + TIntermTyped *movedExpression = mParentLoop->getExpression(); + mReplacements.push_back(NodeUpdateEntry(mParentLoop, movedExpression, nullptr, false)); + TIntermAggregate *body = mParentLoop->getBody(); + TIntermSequence empty; + TIntermSequence insertions; + insertions.push_back(movedExpression); + mInsertions.push_back( + NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, empty, insertions)); + return true; + } + return false; +} + void UnfoldShortCircuitTraverser::nextIteration() { mFoundShortCircuit = false; diff --git a/gfx/angle/src/compiler/translator/VariableInfo.cpp b/gfx/angle/src/compiler/translator/VariableInfo.cpp index 7413dc3b67f1..eb2a2d8d0726 100644 --- a/gfx/angle/src/compiler/translator/VariableInfo.cpp +++ b/gfx/angle/src/compiler/translator/VariableInfo.cpp @@ -551,7 +551,8 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable, ASSERT(blockType); interfaceBlock.name = blockType->name().c_str(); - interfaceBlock.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str(); + interfaceBlock.mappedName = + TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str(); interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : ""); interfaceBlock.arraySize = variable->getArraySize(); interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor); @@ -566,7 +567,7 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable, const TString &fullFieldName = InterfaceBlockFieldName(*blockType, field); const TType &fieldType = *field.type(); - GetVariableTraverser traverser(mSymbolTable); + NameHashingTraverser traverser(mHashFunction, mSymbolTable); traverser.traverse(fieldType, fullFieldName, &interfaceBlock.fields); interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor); diff --git a/gfx/angle/src/compiler/translator/glslang.l b/gfx/angle/src/compiler/translator/glslang.l index c0d92b36545f..d09358dd8ab9 100644 --- a/gfx/angle/src/compiler/translator/glslang.l +++ b/gfx/angle/src/compiler/translator/glslang.l @@ -72,7 +72,7 @@ static int ES2_reserved_ES3_keyword(TParseContext *context, int token); static int ES2_keyword_ES3_reserved(TParseContext *context, int token); static int ES2_ident_ES3_keyword(TParseContext *context, int token); static int uint_constant(TParseContext *context); -static int int_constant(yyscan_t yyscanner); +static int int_constant(TParseContext *context); static int float_constant(yyscan_t yyscanner); static int floatsuffix_check(TParseContext* context); %} @@ -321,9 +321,9 @@ O [0-7] return check_type(yyscanner); } -0[xX]{H}+ { return int_constant(yyscanner); } -0{O}+ { return int_constant(yyscanner); } -{D}+ { return int_constant(yyscanner); } +0[xX]{H}+ { return int_constant(context); } +0{O}+ { return int_constant(context); } +{D}+ { return int_constant(context); } 0[xX]{H}+[uU] { return uint_constant(context); } 0{O}+[uU] { return uint_constant(context); } @@ -478,7 +478,6 @@ int ES2_ident_ES3_keyword(TParseContext *context, int token) int uint_constant(TParseContext *context) { struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - yyscan_t yyscanner = (yyscan_t) context->getScanner(); if (context->getShaderVersion() < 300) { @@ -487,8 +486,8 @@ int uint_constant(TParseContext *context) return 0; } - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + if (!atoi_clamp(yytext, &(yylval->lex.u))) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); return UINTCONSTANT; } @@ -517,11 +516,18 @@ void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* r context->recover(); } -int int_constant(yyscan_t yyscanner) { - struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; +int int_constant(TParseContext *context) { + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + unsigned int u; + if (!atoi_clamp(yytext, &u)) + { + if (context->getShaderVersion() >= 300) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); + else + yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + } + yylval->lex.i = static_cast(u); return INTCONSTANT; } diff --git a/gfx/angle/src/compiler/translator/glslang.y b/gfx/angle/src/compiler/translator/glslang.y index 60aba0c9b9f2..13797bd579db 100644 --- a/gfx/angle/src/compiler/translator/glslang.y +++ b/gfx/angle/src/compiler/translator/glslang.y @@ -215,21 +215,7 @@ identifier variable_identifier : IDENTIFIER { // The symbol table search was done in the lexical phase - const TVariable *variable = context->getNamedVariable(@1, $1.string, $1.symbol); - - if (variable->getType().getQualifier() == EvqConst) - { - TConstantUnion* constArray = variable->getConstPointer(); - TType t(variable->getType()); - $$ = context->intermediate.addConstantUnion(constArray, t, @1); - } - else - { - $$ = context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), - @1); - } + $$ = context->parseVariableIdentifier(@1, $1.string, $1.symbol); // don't delete $1.string, it's used by error recovery, and the pool // pop will reclaim the memory @@ -342,7 +328,7 @@ function_call_header_with_parameters const TType *type = new TType($2->getType()); $1->addParameter(TConstParameter(type)); $$.function = $1; - $$.nodePair.node1 = $2; + $$.nodePair.node1 = context->intermediate.makeAggregate($2, @2); } | function_call_header_with_parameters COMMA assignment_expression { const TType *type = new TType($3->getType()); @@ -573,12 +559,7 @@ expression $$ = $1; } | expression COMMA assignment_expression { - $$ = context->intermediate.addComma($1, $3, @2); - if ($$ == 0) { - context->binaryOpError(@2, ",", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $3; - } + $$ = context->addComma($1, $3, @2); } ; @@ -944,12 +925,12 @@ type_qualifier $$.invariant = true; } | storage_qualifier { - if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) { + if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) + { context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1.qualifier)); context->recover(); - } else { - $$.setBasic(EbtVoid, $1.qualifier, @1); } + $$.setBasic(EbtVoid, $1.qualifier, @1); } | interpolation_qualifier storage_qualifier { $$ = context->joinInterpolationQualifiers(@1, $1.qualifier, @2, $2.qualifier); diff --git a/gfx/angle/src/compiler/translator/glslang_lex.cpp b/gfx/angle/src/compiler/translator/glslang_lex.cpp index ad3f02bce180..35aa6ea0fa26 100644 --- a/gfx/angle/src/compiler/translator/glslang_lex.cpp +++ b/gfx/angle/src/compiler/translator/glslang_lex.cpp @@ -1055,7 +1055,7 @@ static int ES2_reserved_ES3_keyword(TParseContext *context, int token); static int ES2_keyword_ES3_reserved(TParseContext *context, int token); static int ES2_ident_ES3_keyword(TParseContext *context, int token); static int uint_constant(TParseContext *context); -static int int_constant(yyscan_t yyscanner); +static int int_constant(TParseContext *context); static int float_constant(yyscan_t yyscanner); static int floatsuffix_check(TParseContext* context); @@ -1833,15 +1833,15 @@ YY_RULE_SETUP YY_BREAK case 178: YY_RULE_SETUP -{ return int_constant(yyscanner); } +{ return int_constant(context); } YY_BREAK case 179: YY_RULE_SETUP -{ return int_constant(yyscanner); } +{ return int_constant(context); } YY_BREAK case 180: YY_RULE_SETUP -{ return int_constant(yyscanner); } +{ return int_constant(context); } YY_BREAK case 181: YY_RULE_SETUP @@ -3313,7 +3313,6 @@ int ES2_ident_ES3_keyword(TParseContext *context, int token) int uint_constant(TParseContext *context) { struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - yyscan_t yyscanner = (yyscan_t) context->getScanner(); if (context->getShaderVersion() < 300) { @@ -3322,8 +3321,8 @@ int uint_constant(TParseContext *context) return 0; } - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + if (!atoi_clamp(yytext, &(yylval->lex.u))) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); return UINTCONSTANT; } @@ -3352,11 +3351,18 @@ void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* r context->recover(); } -int int_constant(yyscan_t yyscanner) { - struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; +int int_constant(TParseContext *context) { + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + unsigned int u; + if (!atoi_clamp(yytext, &u)) + { + if (context->getShaderVersion() >= 300) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); + else + yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + } + yylval->lex.i = static_cast(u); return INTCONSTANT; } diff --git a/gfx/angle/src/compiler/translator/glslang_tab.cpp b/gfx/angle/src/compiler/translator/glslang_tab.cpp index 6cb8ee16f38c..3fdd8b8211ee 100644 --- a/gfx/angle/src/compiler/translator/glslang_tab.cpp +++ b/gfx/angle/src/compiler/translator/glslang_tab.cpp @@ -694,34 +694,34 @@ static const yytype_uint8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 212, 212, 213, 216, 240, 243, 248, 253, 258, - 263, 269, 272, 275, 278, 281, 284, 290, 298, 309, - 313, 321, 324, 330, 334, 341, 347, 356, 364, 370, - 377, 387, 390, 393, 396, 406, 407, 408, 409, 417, - 418, 421, 424, 431, 432, 435, 441, 442, 446, 453, - 454, 457, 460, 463, 469, 470, 473, 479, 480, 487, - 488, 495, 496, 503, 504, 510, 511, 517, 518, 524, - 525, 531, 532, 540, 541, 542, 543, 547, 548, 549, - 553, 557, 561, 565, 572, 575, 586, 594, 602, 630, - 636, 647, 651, 655, 659, 666, 672, 675, 682, 690, - 711, 738, 748, 776, 781, 791, 796, 806, 809, 812, - 815, 821, 828, 831, 835, 839, 844, 849, 856, 860, - 864, 868, 873, 878, 882, 889, 899, 905, 908, 914, - 920, 927, 936, 946, 954, 957, 964, 968, 972, 977, - 985, 988, 992, 996, 1005, 1014, 1022, 1032, 1044, 1047, - 1050, 1056, 1063, 1066, 1072, 1075, 1078, 1084, 1087, 1092, - 1107, 1111, 1115, 1119, 1123, 1127, 1132, 1137, 1142, 1147, - 1152, 1157, 1162, 1167, 1172, 1177, 1182, 1187, 1192, 1197, - 1202, 1207, 1212, 1217, 1222, 1227, 1232, 1236, 1240, 1244, - 1248, 1252, 1256, 1260, 1264, 1268, 1272, 1276, 1280, 1284, - 1288, 1292, 1300, 1308, 1312, 1325, 1325, 1328, 1328, 1334, - 1337, 1353, 1356, 1365, 1369, 1375, 1382, 1397, 1401, 1405, - 1406, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1422, 1423, - 1423, 1423, 1433, 1434, 1438, 1438, 1439, 1439, 1444, 1447, - 1457, 1460, 1466, 1467, 1471, 1479, 1483, 1490, 1490, 1497, - 1500, 1507, 1512, 1527, 1527, 1532, 1532, 1539, 1539, 1547, - 1550, 1556, 1559, 1565, 1569, 1576, 1579, 1582, 1585, 1588, - 1597, 1601, 1608, 1611, 1617, 1617 + 0, 212, 212, 213, 216, 226, 229, 234, 239, 244, + 249, 255, 258, 261, 264, 267, 270, 276, 284, 295, + 299, 307, 310, 316, 320, 327, 333, 342, 350, 356, + 363, 373, 376, 379, 382, 392, 393, 394, 395, 403, + 404, 407, 410, 417, 418, 421, 427, 428, 432, 439, + 440, 443, 446, 449, 455, 456, 459, 465, 466, 473, + 474, 481, 482, 489, 490, 496, 497, 503, 504, 510, + 511, 517, 518, 526, 527, 528, 529, 533, 534, 535, + 539, 543, 547, 551, 558, 561, 567, 575, 583, 611, + 617, 628, 632, 636, 640, 647, 653, 656, 663, 671, + 692, 719, 729, 757, 762, 772, 777, 787, 790, 793, + 796, 802, 809, 812, 816, 820, 825, 830, 837, 841, + 845, 849, 854, 859, 863, 870, 880, 886, 889, 895, + 901, 908, 917, 927, 935, 938, 945, 949, 953, 958, + 966, 969, 973, 977, 986, 995, 1003, 1013, 1025, 1028, + 1031, 1037, 1044, 1047, 1053, 1056, 1059, 1065, 1068, 1073, + 1088, 1092, 1096, 1100, 1104, 1108, 1113, 1118, 1123, 1128, + 1133, 1138, 1143, 1148, 1153, 1158, 1163, 1168, 1173, 1178, + 1183, 1188, 1193, 1198, 1203, 1208, 1213, 1217, 1221, 1225, + 1229, 1233, 1237, 1241, 1245, 1249, 1253, 1257, 1261, 1265, + 1269, 1273, 1281, 1289, 1293, 1306, 1306, 1309, 1309, 1315, + 1318, 1334, 1337, 1346, 1350, 1356, 1363, 1378, 1382, 1386, + 1387, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1403, 1404, + 1404, 1404, 1414, 1415, 1419, 1419, 1420, 1420, 1425, 1428, + 1438, 1441, 1447, 1448, 1452, 1460, 1464, 1471, 1471, 1478, + 1481, 1488, 1493, 1508, 1508, 1513, 1513, 1520, 1520, 1528, + 1531, 1537, 1540, 1546, 1550, 1557, 1560, 1563, 1566, 1569, + 1578, 1582, 1589, 1592, 1598, 1598 }; #endif @@ -2356,21 +2356,7 @@ yyreduce: { // The symbol table search was done in the lexical phase - const TVariable *variable = context->getNamedVariable((yylsp[0]), (yyvsp[0].lex).string, (yyvsp[0].lex).symbol); - - if (variable->getType().getQualifier() == EvqConst) - { - TConstantUnion* constArray = variable->getConstPointer(); - TType t(variable->getType()); - (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(constArray, t, (yylsp[0])); - } - else - { - (yyval.interm.intermTypedNode) = context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), - (yylsp[0])); - } + (yyval.interm.intermTypedNode) = context->parseVariableIdentifier((yylsp[0]), (yyvsp[0].lex).string, (yyvsp[0].lex).symbol); // don't delete $1.string, it's used by error recovery, and the pool // pop will reclaim the memory @@ -2564,7 +2550,7 @@ yyreduce: const TType *type = new TType((yyvsp[0].interm.intermTypedNode)->getType()); (yyvsp[-1].interm.function)->addParameter(TConstParameter(type)); (yyval.interm).function = (yyvsp[-1].interm.function); - (yyval.interm).nodePair.node1 = (yyvsp[0].interm.intermTypedNode); + (yyval.interm).nodePair.node1 = context->intermediate.makeAggregate((yyvsp[0].interm.intermTypedNode), (yylsp[0])); } break; @@ -3034,12 +3020,7 @@ yyreduce: case 85: { - (yyval.interm.intermTypedNode) = context->intermediate.addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1])); - if ((yyval.interm.intermTypedNode) == 0) { - context->binaryOpError((yylsp[-1]), ",", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); - context->recover(); - (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); - } + (yyval.interm.intermTypedNode) = context->addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1])); } break; @@ -3581,12 +3562,12 @@ yyreduce: case 133: { - if ((yyvsp[0].interm.type).qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) { + if ((yyvsp[0].interm.type).qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) + { context->error((yylsp[0]), "Local variables can only use the const storage qualifier.", getQualifierString((yyvsp[0].interm.type).qualifier)); context->recover(); - } else { - (yyval.interm.type).setBasic(EbtVoid, (yyvsp[0].interm.type).qualifier, (yylsp[0])); } + (yyval.interm.type).setBasic(EbtVoid, (yyvsp[0].interm.type).qualifier, (yylsp[0])); } break; diff --git a/gfx/angle/src/compiler/translator/parseConst.cpp b/gfx/angle/src/compiler/translator/parseConst.cpp deleted file mode 100644 index 0a6f7af8556e..000000000000 --- a/gfx/angle/src/compiler/translator/parseConst.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// -// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// - -#include "compiler/translator/ParseContext.h" - -// -// Use this class to carry along data from node to node in -// the traversal -// -class TConstTraverser : public TIntermTraverser -{ - public: - TConstTraverser(TConstantUnion *cUnion, bool singleConstParam, - TOperator constructType, TInfoSink &sink, TType &t) - : TIntermTraverser(true, false, false), - error(false), - mIndex(0), - mUnionArray(cUnion), - mType(t), - mConstructorType(constructType), - mSingleConstantParam(singleConstParam), - mInfoSink(sink), - mSize(0), - mIsDiagonalMatrixInit(false), - mMatrixCols(0), - mMatrixRows(0) - { - } - - bool error; - - protected: - void visitSymbol(TIntermSymbol *) override; - void visitConstantUnion(TIntermConstantUnion *) override; - bool visitBinary(Visit visit, TIntermBinary *) override; - bool visitUnary(Visit visit, TIntermUnary *) override; - bool visitSelection(Visit visit, TIntermSelection *) override; - bool visitAggregate(Visit visit, TIntermAggregate *) override; - bool visitLoop(Visit visit, TIntermLoop *) override; - bool visitBranch(Visit visit, TIntermBranch *) override; - - size_t mIndex; - TConstantUnion *mUnionArray; - TType mType; - TOperator mConstructorType; - bool mSingleConstantParam; - TInfoSink &mInfoSink; - size_t mSize; // size of the constructor ( 4 for vec4) - bool mIsDiagonalMatrixInit; - int mMatrixCols; // columns of the matrix - int mMatrixRows; // rows of the matrix -}; - -// -// The rest of the file are the traversal functions. The last one -// is the one that starts the traversal. -// -// Return true from interior nodes to have the external traversal -// continue on to children. If you process children yourself, -// return false. -// -void TConstTraverser::visitSymbol(TIntermSymbol *node) -{ - mInfoSink.info.message(EPrefixInternalError, node->getLine(), - "Symbol Node found in constant constructor"); - return; -} - -bool TConstTraverser::visitBinary(Visit visit, TIntermBinary *node) -{ - TQualifier qualifier = node->getType().getQualifier(); - - if (qualifier != EvqConst) - { - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(mType.getCompleteString()); - mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; - } - - mInfoSink.info.message(EPrefixInternalError, node->getLine(), - "Binary Node found in constant constructor"); - return false; -} - -bool TConstTraverser::visitUnary(Visit visit, TIntermUnary *node) -{ - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(mType.getCompleteString()); - mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; -} - -bool TConstTraverser::visitAggregate(Visit visit, TIntermAggregate *node) -{ - if (!node->isConstructor() && node->getOp() != EOpComma) - { - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(mType.getCompleteString()); - mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; - } - - if (node->getSequence()->size() == 0) - { - error = true; - return false; - } - - bool flag = node->getSequence()->size() == 1 && - (*node->getSequence())[0]->getAsTyped()->getAsConstantUnion(); - if (flag) - { - mSingleConstantParam = true; - mConstructorType = node->getOp(); - mSize = node->getType().getObjectSize(); - - if (node->getType().isMatrix()) - { - mIsDiagonalMatrixInit = true; - mMatrixCols = node->getType().getCols(); - mMatrixRows = node->getType().getRows(); - } - } - - for (TIntermSequence::iterator p = node->getSequence()->begin(); - p != node->getSequence()->end(); p++) - { - if (node->getOp() == EOpComma) - mIndex = 0; - (*p)->traverse(this); - } - if (flag) - { - mSingleConstantParam = false; - mConstructorType = EOpNull; - mSize = 0; - mIsDiagonalMatrixInit = false; - mMatrixCols = 0; - mMatrixRows = 0; - } - return false; -} - -bool TConstTraverser::visitSelection(Visit visit, TIntermSelection *node) -{ - mInfoSink.info.message(EPrefixInternalError, node->getLine(), - "Selection Node found in constant constructor"); - error = true; - return false; -} - -void TConstTraverser::visitConstantUnion(TIntermConstantUnion *node) -{ - if (!node->getUnionArrayPointer()) - { - // The constant was not initialized, this should already have been logged - ASSERT(mInfoSink.info.size() != 0); - return; - } - - TConstantUnion *leftUnionArray = mUnionArray; - size_t instanceSize = mType.getObjectSize(); - TBasicType basicType = mType.getBasicType(); - - if (mIndex >= instanceSize) - return; - - if (!mSingleConstantParam) - { - size_t objectSize = node->getType().getObjectSize(); - const TConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - for (size_t i=0; i < objectSize; i++) - { - if (mIndex >= instanceSize) - return; - leftUnionArray[mIndex].cast(basicType, rightUnionArray[i]); - mIndex++; - } - } - else - { - size_t totalSize = mIndex + mSize; - const TConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - if (!mIsDiagonalMatrixInit) - { - int count = 0; - for (size_t i = mIndex; i < totalSize; i++) - { - if (i >= instanceSize) - return; - leftUnionArray[i].cast(basicType, rightUnionArray[count]); - mIndex++; - if (node->getType().getObjectSize() > 1) - count++; - } - } - else - { - // for matrix diagonal constructors from a single scalar - for (int i = 0, col = 0; col < mMatrixCols; col++) - { - for (int row = 0; row < mMatrixRows; row++, i++) - { - if (col == row) - { - leftUnionArray[i].cast(basicType, rightUnionArray[0]); - } - else - { - leftUnionArray[i].setFConst(0.0f); - } - mIndex++; - } - } - } - } -} - -bool TConstTraverser::visitLoop(Visit visit, TIntermLoop *node) -{ - mInfoSink.info.message(EPrefixInternalError, node->getLine(), - "Loop Node found in constant constructor"); - error = true; - return false; -} - -bool TConstTraverser::visitBranch(Visit visit, TIntermBranch *node) -{ - mInfoSink.info.message(EPrefixInternalError, node->getLine(), - "Branch Node found in constant constructor"); - error = true; - return false; -} - -// -// This function is the one to call externally to start the traversal. -// Individual functions can be initialized to 0 to skip processing of that -// type of node. It's children will still be processed. -// -bool TIntermediate::parseConstTree( - const TSourceLoc &line, TIntermNode *root, TConstantUnion *unionArray, - TOperator constructorType, TType t, bool singleConstantParam) -{ - if (root == 0) - return false; - - TConstTraverser it(unionArray, singleConstantParam, constructorType, - mInfoSink, t); - - root->traverse(&it); - if (it.error) - return true; - else - return false; -} diff --git a/gfx/angle/src/compiler/translator/util.cpp b/gfx/angle/src/compiler/translator/util.cpp index 6af6557c7c25..013113720665 100644 --- a/gfx/angle/src/compiler/translator/util.cpp +++ b/gfx/angle/src/compiler/translator/util.cpp @@ -20,11 +20,11 @@ bool strtof_clamp(const std::string &str, float *value) return success; } -bool atoi_clamp(const char *str, int *value) +bool atoi_clamp(const char *str, unsigned int *value) { bool success = pp::numeric_lex_int(str, value); if (!success) - *value = std::numeric_limits::max(); + *value = std::numeric_limits::max(); return success; } diff --git a/gfx/angle/src/compiler/translator/util.h b/gfx/angle/src/compiler/translator/util.h index 15b6fb5cc09f..ea7a35a352a0 100644 --- a/gfx/angle/src/compiler/translator/util.h +++ b/gfx/angle/src/compiler/translator/util.h @@ -20,9 +20,9 @@ // Return false if overflow happens. bool strtof_clamp(const std::string &str, float *value); -// If overflow happens, clamp the value to INT_MIN or INT_MAX. +// If overflow happens, clamp the value to UINT_MIN or UINT_MAX. // Return false if overflow happens. -bool atoi_clamp(const char *str, int *value); +bool atoi_clamp(const char *str, unsigned int *value); class TSymbolTable; diff --git a/gfx/angle/src/libANGLE/Caps.cpp b/gfx/angle/src/libANGLE/Caps.cpp index 98dc17625fca..841be969595c 100644 --- a/gfx/angle/src/libANGLE/Caps.cpp +++ b/gfx/angle/src/libANGLE/Caps.cpp @@ -110,6 +110,9 @@ Extensions::Extensions() textureCompressionDXT1(false), textureCompressionDXT3(false), textureCompressionDXT5(false), + textureCompressionASTCHDR(false), + textureCompressionASTCLDR(false), + compressedETC1RGB8Texture(false), depthTextures(false), textureStorage(false), textureNPOT(false), @@ -141,6 +144,7 @@ Extensions::Extensions() eglImageExternalEssl3(false), unpackSubimage(false), packSubimage(false), + vertexArrayObject(false), colorBufferFloat(false) { } @@ -150,56 +154,60 @@ std::vector Extensions::getStrings() const std::vector extensionStrings; // clang-format off - // | Extension name | Supported flag | Output vector | - InsertExtensionString("GL_OES_element_index_uint", elementIndexUint, &extensionStrings); - InsertExtensionString("GL_OES_packed_depth_stencil", packedDepthStencil, &extensionStrings); - InsertExtensionString("GL_OES_get_program_binary", getProgramBinary, &extensionStrings); - InsertExtensionString("GL_OES_rgb8_rgba8", rgb8rgba8, &extensionStrings); - InsertExtensionString("GL_EXT_texture_format_BGRA8888", textureFormatBGRA8888, &extensionStrings); - InsertExtensionString("GL_EXT_read_format_bgra", readFormatBGRA, &extensionStrings); - InsertExtensionString("GL_NV_pixel_buffer_object", pixelBufferObject, &extensionStrings); - InsertExtensionString("GL_OES_mapbuffer", mapBuffer, &extensionStrings); - InsertExtensionString("GL_EXT_map_buffer_range", mapBufferRange, &extensionStrings); - InsertExtensionString("GL_OES_texture_half_float", textureHalfFloat, &extensionStrings); - InsertExtensionString("GL_OES_texture_half_float_linear", textureHalfFloatLinear, &extensionStrings); - InsertExtensionString("GL_OES_texture_float", textureFloat, &extensionStrings); - InsertExtensionString("GL_OES_texture_float_linear", textureFloatLinear, &extensionStrings); - InsertExtensionString("GL_EXT_texture_rg", textureRG, &extensionStrings); - InsertExtensionString("GL_EXT_texture_compression_dxt1", textureCompressionDXT1, &extensionStrings); - InsertExtensionString("GL_ANGLE_texture_compression_dxt3", textureCompressionDXT3, &extensionStrings); - InsertExtensionString("GL_ANGLE_texture_compression_dxt5", textureCompressionDXT5, &extensionStrings); - InsertExtensionString("GL_EXT_sRGB", sRGB, &extensionStrings); - InsertExtensionString("GL_ANGLE_depth_texture", depthTextures, &extensionStrings); - InsertExtensionString("GL_EXT_texture_storage", textureStorage, &extensionStrings); - InsertExtensionString("GL_OES_texture_npot", textureNPOT, &extensionStrings); - InsertExtensionString("GL_EXT_draw_buffers", drawBuffers, &extensionStrings); - InsertExtensionString("GL_EXT_texture_filter_anisotropic", textureFilterAnisotropic, &extensionStrings); - InsertExtensionString("GL_EXT_occlusion_query_boolean", occlusionQueryBoolean, &extensionStrings); - InsertExtensionString("GL_NV_fence", fence, &extensionStrings); - InsertExtensionString("GL_ANGLE_timer_query", timerQuery, &extensionStrings); - InsertExtensionString("GL_EXT_robustness", robustness, &extensionStrings); - InsertExtensionString("GL_EXT_blend_minmax", blendMinMax, &extensionStrings); - InsertExtensionString("GL_ANGLE_framebuffer_blit", framebufferBlit, &extensionStrings); - InsertExtensionString("GL_ANGLE_framebuffer_multisample", framebufferMultisample, &extensionStrings); - InsertExtensionString("GL_ANGLE_instanced_arrays", instancedArrays, &extensionStrings); - InsertExtensionString("GL_ANGLE_pack_reverse_row_order", packReverseRowOrder, &extensionStrings); - InsertExtensionString("GL_OES_standard_derivatives", standardDerivatives, &extensionStrings); - InsertExtensionString("GL_EXT_shader_texture_lod", shaderTextureLOD, &extensionStrings); - InsertExtensionString("GL_NV_shader_framebuffer_fetch", NVshaderFramebufferFetch, &extensionStrings); - InsertExtensionString("GL_ARM_shader_framebuffer_fetch", ARMshaderFramebufferFetch,&extensionStrings); - InsertExtensionString("GL_EXT_shader_framebuffer_fetch", shaderFramebufferFetch, &extensionStrings); - InsertExtensionString("GL_EXT_frag_depth", fragDepth, &extensionStrings); - InsertExtensionString("GL_ANGLE_texture_usage", textureUsage, &extensionStrings); - InsertExtensionString("GL_ANGLE_translated_shader_source", translatedShaderSource, &extensionStrings); - InsertExtensionString("GL_OES_fbo_render_mipmap", fboRenderMipmap, &extensionStrings); - InsertExtensionString("GL_EXT_discard_framebuffer", discardFramebuffer, &extensionStrings); - InsertExtensionString("GL_EXT_debug_marker", debugMarker, &extensionStrings); - InsertExtensionString("GL_OES_EGL_image", eglImage, &extensionStrings); - InsertExtensionString("GL_OES_EGL_image_external", eglImageExternal, &extensionStrings); - InsertExtensionString("GL_OES_EGL_image_external_essl3", eglImageExternalEssl3, &extensionStrings); - InsertExtensionString("GL_EXT_unpack_subimage", unpackSubimage, &extensionStrings); - InsertExtensionString("GL_NV_pack_subimage", packSubimage, &extensionStrings); - InsertExtensionString("GL_EXT_color_buffer_float", colorBufferFloat, &extensionStrings); + // | Extension name | Supported flag | Output vector | + InsertExtensionString("GL_OES_element_index_uint", elementIndexUint, &extensionStrings); + InsertExtensionString("GL_OES_packed_depth_stencil", packedDepthStencil, &extensionStrings); + InsertExtensionString("GL_OES_get_program_binary", getProgramBinary, &extensionStrings); + InsertExtensionString("GL_OES_rgb8_rgba8", rgb8rgba8, &extensionStrings); + InsertExtensionString("GL_EXT_texture_format_BGRA8888", textureFormatBGRA8888, &extensionStrings); + InsertExtensionString("GL_EXT_read_format_bgra", readFormatBGRA, &extensionStrings); + InsertExtensionString("GL_NV_pixel_buffer_object", pixelBufferObject, &extensionStrings); + InsertExtensionString("GL_OES_mapbuffer", mapBuffer, &extensionStrings); + InsertExtensionString("GL_EXT_map_buffer_range", mapBufferRange, &extensionStrings); + InsertExtensionString("GL_OES_texture_half_float", textureHalfFloat, &extensionStrings); + InsertExtensionString("GL_OES_texture_half_float_linear", textureHalfFloatLinear, &extensionStrings); + InsertExtensionString("GL_OES_texture_float", textureFloat, &extensionStrings); + InsertExtensionString("GL_OES_texture_float_linear", textureFloatLinear, &extensionStrings); + InsertExtensionString("GL_EXT_texture_rg", textureRG, &extensionStrings); + InsertExtensionString("GL_EXT_texture_compression_dxt1", textureCompressionDXT1, &extensionStrings); + InsertExtensionString("GL_ANGLE_texture_compression_dxt3", textureCompressionDXT3, &extensionStrings); + InsertExtensionString("GL_ANGLE_texture_compression_dxt5", textureCompressionDXT5, &extensionStrings); + InsertExtensionString("GL_KHR_texture_compression_astc_hdr", textureCompressionASTCHDR, &extensionStrings); + InsertExtensionString("GL_KHR_texture_compression_astc_ldr", textureCompressionASTCLDR, &extensionStrings); + InsertExtensionString("GL_OES_compressed_ETC1_RGB8_texture", compressedETC1RGB8Texture, &extensionStrings); + InsertExtensionString("GL_EXT_sRGB", sRGB, &extensionStrings); + InsertExtensionString("GL_ANGLE_depth_texture", depthTextures, &extensionStrings); + InsertExtensionString("GL_EXT_texture_storage", textureStorage, &extensionStrings); + InsertExtensionString("GL_OES_texture_npot", textureNPOT, &extensionStrings); + InsertExtensionString("GL_EXT_draw_buffers", drawBuffers, &extensionStrings); + InsertExtensionString("GL_EXT_texture_filter_anisotropic", textureFilterAnisotropic, &extensionStrings); + InsertExtensionString("GL_EXT_occlusion_query_boolean", occlusionQueryBoolean, &extensionStrings); + InsertExtensionString("GL_NV_fence", fence, &extensionStrings); + InsertExtensionString("GL_ANGLE_timer_query", timerQuery, &extensionStrings); + InsertExtensionString("GL_EXT_robustness", robustness, &extensionStrings); + InsertExtensionString("GL_EXT_blend_minmax", blendMinMax, &extensionStrings); + InsertExtensionString("GL_ANGLE_framebuffer_blit", framebufferBlit, &extensionStrings); + InsertExtensionString("GL_ANGLE_framebuffer_multisample", framebufferMultisample, &extensionStrings); + InsertExtensionString("GL_ANGLE_instanced_arrays", instancedArrays, &extensionStrings); + InsertExtensionString("GL_ANGLE_pack_reverse_row_order", packReverseRowOrder, &extensionStrings); + InsertExtensionString("GL_OES_standard_derivatives", standardDerivatives, &extensionStrings); + InsertExtensionString("GL_EXT_shader_texture_lod", shaderTextureLOD, &extensionStrings); + InsertExtensionString("GL_NV_shader_framebuffer_fetch", NVshaderFramebufferFetch, &extensionStrings); + InsertExtensionString("GL_ARM_shader_framebuffer_fetch", ARMshaderFramebufferFetch, &extensionStrings); + InsertExtensionString("GL_EXT_shader_framebuffer_fetch", shaderFramebufferFetch, &extensionStrings); + InsertExtensionString("GL_EXT_frag_depth", fragDepth, &extensionStrings); + InsertExtensionString("GL_ANGLE_texture_usage", textureUsage, &extensionStrings); + InsertExtensionString("GL_ANGLE_translated_shader_source", translatedShaderSource, &extensionStrings); + InsertExtensionString("GL_OES_fbo_render_mipmap", fboRenderMipmap, &extensionStrings); + InsertExtensionString("GL_EXT_discard_framebuffer", discardFramebuffer, &extensionStrings); + InsertExtensionString("GL_EXT_debug_marker", debugMarker, &extensionStrings); + InsertExtensionString("GL_OES_EGL_image", eglImage, &extensionStrings); + InsertExtensionString("GL_OES_EGL_image_external", eglImageExternal, &extensionStrings); + InsertExtensionString("GL_OES_EGL_image_external_essl3", eglImageExternalEssl3, &extensionStrings); + InsertExtensionString("GL_EXT_unpack_subimage", unpackSubimage, &extensionStrings); + InsertExtensionString("GL_NV_pack_subimage", packSubimage, &extensionStrings); + InsertExtensionString("GL_EXT_color_buffer_float", colorBufferFloat, &extensionStrings); + InsertExtensionString("GL_OES_vertex_array_object", vertexArrayObject, &extensionStrings); // clang-format on return extensionStrings; @@ -357,6 +365,51 @@ static bool DetermineDXT5TextureSupport(const TextureCapsMap &textureCaps) return GetFormatSupport(textureCaps, requiredFormats, true, true, false); } +// Check for GL_KHR_texture_compression_astc_hdr and GL_KHR_texture_compression_astc_ldr +static bool DetermineASTCTextureSupport(const TextureCapsMap &textureCaps) +{ + std::vector requiredFormats; + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_4x4_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_5x4_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_5x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_6x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_6x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_8x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_8x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_8x8_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_10x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_10x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_10x8_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_10x10_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_12x10_KHR); + requiredFormats.push_back(GL_COMPRESSED_RGBA_ASTC_12x12_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR); + requiredFormats.push_back(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR); + + return GetFormatSupport(textureCaps, requiredFormats, true, true, false); +} + +// Check for GL_ETC1_RGB8_OES +static bool DetermineETC1RGB8TextureSupport(const TextureCapsMap &textureCaps) +{ + std::vector requiredFormats; + requiredFormats.push_back(GL_ETC1_RGB8_OES); + + return GetFormatSupport(textureCaps, requiredFormats, true, true, false); +} + // Check for GL_ANGLE_texture_compression_dxt5 static bool DetermineSRGBTextureSupport(const TextureCapsMap &textureCaps) { @@ -410,6 +463,9 @@ void Extensions::setTextureExtensionSupport(const TextureCapsMap &textureCaps) textureCompressionDXT1 = DetermineDXT1TextureSupport(textureCaps); textureCompressionDXT3 = DetermineDXT3TextureSupport(textureCaps); textureCompressionDXT5 = DetermineDXT5TextureSupport(textureCaps); + textureCompressionASTCHDR = DetermineASTCTextureSupport(textureCaps); + textureCompressionASTCLDR = textureCompressionASTCHDR; + compressedETC1RGB8Texture = DetermineETC1RGB8TextureSupport(textureCaps); sRGB = DetermineSRGBTextureSupport(textureCaps); depthTextures = DetermineDepthTextureSupport(textureCaps); colorBufferFloat = DetermineColorBufferFloatSupport(textureCaps); diff --git a/gfx/angle/src/libANGLE/Caps.h b/gfx/angle/src/libANGLE/Caps.h index 7479d17e889b..3e1c1d1cb24b 100644 --- a/gfx/angle/src/libANGLE/Caps.h +++ b/gfx/angle/src/libANGLE/Caps.h @@ -80,6 +80,8 @@ struct Extensions // GL_OES_texture_float, GL_OES_texture_float_linear // GL_EXT_texture_rg // GL_EXT_texture_compression_dxt1, GL_ANGLE_texture_compression_dxt3, GL_ANGLE_texture_compression_dxt5 + // GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr + // GL_OES_compressed_ETC1_RGB8_texture // GL_EXT_sRGB // GL_ANGLE_depth_texture // GL_EXT_color_buffer_float @@ -138,6 +140,16 @@ struct Extensions bool textureCompressionDXT3; bool textureCompressionDXT5; + // GL_KHR_texture_compression_astc_hdr + bool textureCompressionASTCHDR; + + // GL_KHR_texture_compression_astc_ldr + bool textureCompressionASTCLDR; + + // GL_OES_compressed_ETC1_RGB8_texture + // Implies that TextureCaps for GL_ETC1_RGB8_OES exist + bool compressedETC1RGB8Texture; + // GL_EXT_sRGB // Implies that TextureCaps for GL_SRGB8_ALPHA8 and GL_SRGB8 exist // TODO: Don't advertise this extension in ES3 @@ -234,6 +246,9 @@ struct Extensions // NV_pack_subimage bool packSubimage; + // GL_OES_vertex_array_object + bool vertexArrayObject; + // ES3 Extension support // GL_EXT_color_buffer_float diff --git a/gfx/angle/src/libANGLE/Context.cpp b/gfx/angle/src/libANGLE/Context.cpp index fb1683e68466..1a1bcd2187bf 100644 --- a/gfx/angle/src/libANGLE/Context.cpp +++ b/gfx/angle/src/libANGLE/Context.cpp @@ -38,7 +38,7 @@ namespace void MarkTransformFeedbackBufferUsage(gl::TransformFeedback *transformFeedback) { - if (transformFeedback->isActive() && !transformFeedback->isPaused()) + if (transformFeedback && transformFeedback->isActive() && !transformFeedback->isPaused()) { for (size_t tfBufferIndex = 0; tfBufferIndex < transformFeedback->getIndexedBufferCount(); tfBufferIndex++) @@ -63,16 +63,16 @@ Context::Context(const egl::Config *config, rx::Renderer *renderer, bool notifyResets, bool robustAccess) - : mRenderer(renderer), + : ValidationContext(clientVersion, + mState, + mCaps, + mTextureCaps, + mExtensions, + nullptr, + mLimitations), + mRenderer(renderer), mConfig(config), - mCurrentSurface(nullptr), - mData(reinterpret_cast(this), - clientVersion, - mState, - mCaps, - mTextureCaps, - mExtensions, - nullptr) + mCurrentSurface(nullptr) { ASSERT(robustAccess == false); // Unimplemented @@ -138,12 +138,16 @@ Context::Context(const egl::Config *config, bindPixelPackBuffer(0); bindPixelUnpackBuffer(0); - // [OpenGL ES 3.0.2] section 2.14.1 pg 85: - // In the initial state, a default transform feedback object is bound and treated as - // a transform feedback object with a name of zero. That object is bound any time - // BindTransformFeedback is called with id of zero - mTransformFeedbackZero.set(new TransformFeedback(mRenderer->createTransformFeedback(), 0, mCaps)); - bindTransformFeedback(0); + if (mClientVersion >= 3) + { + // [OpenGL ES 3.0.2] section 2.14.1 pg 85: + // In the initial state, a default transform feedback object is bound and treated as + // a transform feedback object with a name of zero. That object is bound any time + // BindTransformFeedback is called with id of zero + mTransformFeedbackZero.set( + new TransformFeedback(mRenderer->createTransformFeedback(), 0, mCaps)); + bindTransformFeedback(0); + } mHasBeenCurrent = false; mContextLost = false; @@ -188,7 +192,7 @@ Context::~Context() mTransformFeedbackZero.set(NULL); for (auto transformFeedback : mTransformFeedbackMap) { - SafeDelete(transformFeedback.second); + transformFeedback.second->release(); } for (auto &zeroTexture : mZeroTextures) @@ -1103,26 +1107,6 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu } } return true; - case GL_PACK_ROW_LENGTH: - case GL_PACK_SKIP_ROWS: - case GL_PACK_SKIP_PIXELS: - if ((mClientVersion < 3) && !mExtensions.packSubimage) - { - return false; - } - *type = GL_INT; - *numParams = 1; - return true; - case GL_UNPACK_ROW_LENGTH: - case GL_UNPACK_SKIP_ROWS: - case GL_UNPACK_SKIP_PIXELS: - if ((mClientVersion < 3) && !mExtensions.unpackSubimage) - { - return false; - } - *type = GL_INT; - *numParams = 1; - return true; case GL_MAX_VIEWPORT_DIMS: { *type = GL_INT; @@ -1195,6 +1179,39 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu return true; } + // Check for ES3.0+ parameter names which are also exposed as ES2 extensions + switch (pname) + { + case GL_PACK_ROW_LENGTH: + case GL_PACK_SKIP_ROWS: + case GL_PACK_SKIP_PIXELS: + if ((mClientVersion < 3) && !mExtensions.packSubimage) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_SKIP_ROWS: + case GL_UNPACK_SKIP_PIXELS: + if ((mClientVersion < 3) && !mExtensions.unpackSubimage) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_VERTEX_ARRAY_BINDING: + if ((mClientVersion < 3) && !mExtensions.vertexArrayObject) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + if (mClientVersion < 3) { return false; @@ -1220,7 +1237,6 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu case GL_MAX_VERTEX_OUTPUT_COMPONENTS: case GL_MAX_FRAGMENT_INPUT_COMPONENTS: case GL_MAX_VARYING_COMPONENTS: - case GL_VERTEX_ARRAY_BINDING: case GL_MAX_VERTEX_UNIFORM_COMPONENTS: case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: case GL_MIN_PROGRAM_TEXEL_OFFSET: @@ -1233,14 +1249,8 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: - case GL_PACK_ROW_LENGTH: - case GL_PACK_SKIP_ROWS: - case GL_PACK_SKIP_PIXELS: - case GL_UNPACK_ROW_LENGTH: case GL_UNPACK_IMAGE_HEIGHT: case GL_UNPACK_SKIP_IMAGES: - case GL_UNPACK_SKIP_ROWS: - case GL_UNPACK_SKIP_PIXELS: { *type = GL_INT; *numParams = 1; @@ -1260,6 +1270,7 @@ bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *nu case GL_TRANSFORM_FEEDBACK_ACTIVE: case GL_TRANSFORM_FEEDBACK_PAUSED: + case GL_PRIMITIVE_RESTART_FIXED_INDEX: { *type = GL_BOOL; *numParams = 1; @@ -1454,11 +1465,6 @@ bool Context::isResetNotificationEnabled() return (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT); } -int Context::getClientVersion() const -{ - return mClientVersion; -} - const egl::Config *Context::getConfig() const { return mConfig; @@ -1486,26 +1492,6 @@ EGLenum Context::getRenderBuffer() const } } -const Caps &Context::getCaps() const -{ - return mCaps; -} - -const TextureCapsMap &Context::getTextureCaps() const -{ - return mTextureCaps; -} - -const Extensions &Context::getExtensions() const -{ - return mExtensions; -} - -const Limitations &Context::getLimitations() const -{ - return mLimitations; -} - void Context::detachTexture(GLuint texture) { // Simple pass-through to State's detachTexture method, as textures do not require diff --git a/gfx/angle/src/libANGLE/Context.h b/gfx/angle/src/libANGLE/Context.h index 7e92c81ee875..44dbd962e6e8 100644 --- a/gfx/angle/src/libANGLE/Context.h +++ b/gfx/angle/src/libANGLE/Context.h @@ -55,7 +55,7 @@ class VertexArray; class Sampler; class TransformFeedback; -class Context final : angle::NonCopyable +class Context final : public ValidationContext { public: Context(const egl::Config *config, int clientVersion, const Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess); @@ -193,23 +193,16 @@ class Context final : angle::NonCopyable void pushGroupMarker(GLsizei length, const char *marker); void popGroupMarker(); - void recordError(const Error &error); + void recordError(const Error &error) override; GLenum getError(); GLenum getResetStatus(); virtual bool isResetNotificationEnabled(); - virtual int getClientVersion() const; - const egl::Config *getConfig() const; EGLenum getClientType() const; EGLenum getRenderBuffer() const; - const Caps &getCaps() const; - const TextureCapsMap &getTextureCaps() const; - const Extensions &getExtensions() const; - const Limitations &getLimitations() const; - const std::string &getRendererString() const; const std::string &getExtensionString() const; @@ -219,9 +212,7 @@ class Context final : angle::NonCopyable rx::Renderer *getRenderer() { return mRenderer; } State &getState() { return mState; } - const State &getState() const { return mState; } - const Data &getData() const { return mData; } void syncRendererState(); void syncRendererState(const State::DirtyBits &bitMask); @@ -296,9 +287,6 @@ class Context final : angle::NonCopyable egl::Surface *mCurrentSurface; ResourceManager *mResourceManager; - - // Cache the Data object to avoid re-calling the constructor - Data mData; }; } diff --git a/gfx/angle/src/libANGLE/Data.cpp b/gfx/angle/src/libANGLE/Data.cpp index fc9e001cf839..57657c40b420 100644 --- a/gfx/angle/src/libANGLE/Data.cpp +++ b/gfx/angle/src/libANGLE/Data.cpp @@ -18,18 +18,37 @@ Data::Data(uintptr_t contextIn, const Caps &capsIn, const TextureCapsMap &textureCapsIn, const Extensions &extensionsIn, - const ResourceManager *resourceManagerIn) + const ResourceManager *resourceManagerIn, + const Limitations &limitationsIn) : context(contextIn), clientVersion(clientVersionIn), state(&stateIn), caps(&capsIn), textureCaps(&textureCapsIn), extensions(&extensionsIn), - resourceManager(resourceManagerIn) + resourceManager(resourceManagerIn), + limitations(&limitationsIn) {} Data::~Data() { } +ValidationContext::ValidationContext(GLint clientVersion, + const State &state, + const Caps &caps, + const TextureCapsMap &textureCaps, + const Extensions &extensions, + const ResourceManager *resourceManager, + const Limitations &limitations) + : mData(reinterpret_cast(this), + clientVersion, + state, + caps, + textureCaps, + extensions, + resourceManager, + limitations) +{ +} } diff --git a/gfx/angle/src/libANGLE/Data.h b/gfx/angle/src/libANGLE/Data.h index b8cd784a4da9..eac5ffed6a1d 100644 --- a/gfx/angle/src/libANGLE/Data.h +++ b/gfx/angle/src/libANGLE/Data.h @@ -24,7 +24,8 @@ struct Data final : public angle::NonCopyable const Caps &caps, const TextureCapsMap &textureCaps, const Extensions &extensions, - const ResourceManager *resourceManager); + const ResourceManager *resourceManager, + const Limitations &limitations); ~Data(); uintptr_t context; @@ -34,6 +35,33 @@ struct Data final : public angle::NonCopyable const TextureCapsMap *textureCaps; const Extensions *extensions; const ResourceManager *resourceManager; + const Limitations *limitations; +}; + +class ValidationContext : angle::NonCopyable +{ + public: + ValidationContext(GLint clientVersion, + const State &state, + const Caps &caps, + const TextureCapsMap &textureCaps, + const Extensions &extensions, + const ResourceManager *resourceManager, + const Limitations &limitations); + virtual ~ValidationContext() {} + + virtual void recordError(const Error &error) = 0; + + const Data &getData() const { return mData; } + int getClientVersion() const { return mData.clientVersion; } + const State &getState() const { return *mData.state; } + const Caps &getCaps() const { return *mData.caps; } + const TextureCapsMap &getTextureCaps() const { return *mData.textureCaps; } + const Extensions &getExtensions() const { return *mData.extensions; } + const Limitations &getLimitations() const { return *mData.limitations; } + + protected: + Data mData; }; } diff --git a/gfx/angle/src/libANGLE/Device.cpp b/gfx/angle/src/libANGLE/Device.cpp index 980ec0930fa4..d00bf4ddaa76 100644 --- a/gfx/angle/src/libANGLE/Device.cpp +++ b/gfx/angle/src/libANGLE/Device.cpp @@ -45,7 +45,10 @@ Device::~Device() Error Device::getDevice(EGLAttrib *value) { - return getImplementation()->getDevice(value); + void *nativeDevice = nullptr; + egl::Error error = getImplementation()->getDevice(&nativeDevice); + *value = reinterpret_cast(nativeDevice); + return error; } EGLint Device::getType() diff --git a/gfx/angle/src/libANGLE/Error.cpp b/gfx/angle/src/libANGLE/Error.cpp index e17f26bec4ae..00241451f5a8 100644 --- a/gfx/angle/src/libANGLE/Error.cpp +++ b/gfx/angle/src/libANGLE/Error.cpp @@ -41,6 +41,23 @@ const std::string &Error::getMessage() const return *mMessage; } +bool Error::operator==(const Error &other) const +{ + if (mCode != other.mCode) + return false; + + // TODO(jmadill): Compare extended error codes instead of strings. + if ((mMessage == nullptr || other.mMessage == nullptr) && + ((mMessage == nullptr) != (other.mMessage == nullptr))) + return false; + + return (*mMessage == *other.mMessage); +} + +bool Error::operator!=(const Error &other) const +{ + return !(*this == other); +} } namespace egl diff --git a/gfx/angle/src/libANGLE/Error.h b/gfx/angle/src/libANGLE/Error.h index 581294343736..81c9330fc088 100644 --- a/gfx/angle/src/libANGLE/Error.h +++ b/gfx/angle/src/libANGLE/Error.h @@ -35,6 +35,10 @@ class Error final const std::string &getMessage() const; + // Useful for mocking and testing + bool operator==(const Error &other) const; + bool operator!=(const Error &other) const; + private: void createMessageString() const; diff --git a/gfx/angle/src/libANGLE/Platform.cpp b/gfx/angle/src/libANGLE/Platform.cpp index ab75bbba5af8..bfcdb1494ea8 100644 --- a/gfx/angle/src/libANGLE/Platform.cpp +++ b/gfx/angle/src/libANGLE/Platform.cpp @@ -16,20 +16,20 @@ angle::Platform *currentPlatform = nullptr; } // static -ANGLE_EXPORT angle::Platform *ANGLEPlatformCurrent() +angle::Platform *ANGLE_APIENTRY ANGLEPlatformCurrent() { return currentPlatform; } // static -ANGLE_EXPORT void ANGLEPlatformInitialize(angle::Platform *platformImpl) +void ANGLE_APIENTRY ANGLEPlatformInitialize(angle::Platform *platformImpl) { ASSERT(platformImpl != nullptr); currentPlatform = platformImpl; } // static -ANGLE_EXPORT void ANGLEPlatformShutdown() +void ANGLE_APIENTRY ANGLEPlatformShutdown() { currentPlatform = nullptr; } diff --git a/gfx/angle/src/libANGLE/Program.cpp b/gfx/angle/src/libANGLE/Program.cpp index 16be6879c962..9347419db884 100644 --- a/gfx/angle/src/libANGLE/Program.cpp +++ b/gfx/angle/src/libANGLE/Program.cpp @@ -222,20 +222,10 @@ VariableLocation::VariableLocation(const std::string &name, unsigned int element { } -LinkedVarying::LinkedVarying() -{ -} - -LinkedVarying::LinkedVarying(const std::string &name, GLenum type, GLsizei size, const std::string &semanticName, - unsigned int semanticIndex, unsigned int semanticIndexCount) - : name(name), type(type), size(size), semanticName(semanticName), semanticIndex(semanticIndex), semanticIndexCount(semanticIndexCount) -{ -} - Program::Data::Data() : mAttachedFragmentShader(nullptr), mAttachedVertexShader(nullptr), - mTransformFeedbackBufferMode(GL_NONE) + mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS) { } @@ -1682,7 +1672,8 @@ void Program::indexUniforms() bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform) { - if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, true)) + // We don't validate precision on UBO fields. See resolution of Khronos bug 10287. + if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, false)) { return false; } @@ -2014,6 +2005,12 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog, } uniqueNames.insert(tfVaryingName); + if (varying->isArray()) + { + infoLog << "Capture of arrays is undefined and not supported."; + return false; + } + // TODO(jmadill): Investigate implementation limits on D3D11 size_t componentCount = gl::VariableComponentCount(varying->type); if (mData.mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS && @@ -2031,10 +2028,9 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog, } } - // TODO(jmadill): investigate if we can support capturing array elements. if (tfVaryingName.find('[') != std::string::npos) { - infoLog << "Capture of array elements not currently supported."; + infoLog << "Capture of array elements is undefined and not supported."; return false; } @@ -2245,7 +2241,11 @@ Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable } unsigned int elementCount = uniform.elementCount(); - vectorAndSamplerCount.vectorCount = (VariableRegisterCount(uniform.type) * elementCount); + + // Samplers aren't "real" uniforms, so they don't count towards register usage. + // Likewise, don't count "real" uniforms towards sampler count. + vectorAndSamplerCount.vectorCount = + (isSampler ? 0 : (VariableRegisterCount(uniform.type) * elementCount)); vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0); return vectorAndSamplerCount; diff --git a/gfx/angle/src/libANGLE/Program.h b/gfx/angle/src/libANGLE/Program.h index e4b19f0562b9..ac06ff27516e 100644 --- a/gfx/angle/src/libANGLE/Program.h +++ b/gfx/angle/src/libANGLE/Program.h @@ -143,24 +143,6 @@ struct VariableLocation unsigned int index; }; -struct LinkedVarying -{ - LinkedVarying(); - LinkedVarying(const std::string &name, GLenum type, GLsizei size, const std::string &semanticName, - unsigned int semanticIndex, unsigned int semanticIndexCount); - - // Original GL name - std::string name; - - GLenum type; - GLsizei size; - - // DirectX semantic information - std::string semanticName; - unsigned int semanticIndex; - unsigned int semanticIndexCount; -}; - class Program : angle::NonCopyable { public: diff --git a/gfx/angle/src/libANGLE/RefCountObject.h b/gfx/angle/src/libANGLE/RefCountObject.h index b853c9d82cdf..6250834ec52a 100644 --- a/gfx/angle/src/libANGLE/RefCountObject.h +++ b/gfx/angle/src/libANGLE/RefCountObject.h @@ -76,6 +76,13 @@ class BindingPointer GLuint id() const { return (mObject != nullptr) ? mObject->id() : 0; } + bool operator==(const BindingPointer &other) const + { + return mObject == other.mObject; + } + + bool operator!=(const BindingPointer &other) const { return !(*this == other); } + private: ObjectType *mObject; }; @@ -103,6 +110,16 @@ class OffsetBindingPointer : public BindingPointer GLintptr getOffset() const { return mOffset; } GLsizeiptr getSize() const { return mSize; } + bool operator==(const OffsetBindingPointer &other) const + { + return this->get() == other.get() && mOffset == other.mOffset && mSize == other.mSize; + } + + bool operator!=(const OffsetBindingPointer &other) const + { + return !(*this == other); + } + private: GLintptr mOffset; GLsizeiptr mSize; diff --git a/gfx/angle/src/libANGLE/Shader.cpp b/gfx/angle/src/libANGLE/Shader.cpp index 98fcdca80244..6cfa4b9f1bdb 100644 --- a/gfx/angle/src/libANGLE/Shader.cpp +++ b/gfx/angle/src/libANGLE/Shader.cpp @@ -48,8 +48,10 @@ const std::vector &GetShaderVariables(const std::vector *variableLis return *variableList; } +} // anonymous namespace + // true if varying x has a higher priority in packing than y -bool CompareVarying(const sh::Varying &x, const sh::Varying &y) +bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y) { if (x.type == y.type) { @@ -70,8 +72,6 @@ bool CompareVarying(const sh::Varying &x, const sh::Varying &y) return gl::VariableSortOrder(x.type) < gl::VariableSortOrder(y.type); } -} // anonymous namespace - Shader::Data::Data(GLenum shaderType) : mShaderType(shaderType), mShaderVersion(100) { } @@ -170,6 +170,17 @@ int Shader::getTranslatedSourceLength() const return (static_cast(mData.mTranslatedSource.length()) + 1); } +int Shader::getTranslatedSourceWithDebugInfoLength() const +{ + const std::string &debugInfo = mImplementation->getDebugInfo(); + if (debugInfo.empty()) + { + return 0; + } + + return (static_cast(debugInfo.length()) + 1); +} + void Shader::getSourceImpl(const std::string &source, GLsizei bufSize, GLsizei *length, char *buffer) { int index = 0; @@ -200,7 +211,7 @@ void Shader::getTranslatedSource(GLsizei bufSize, GLsizei *length, char *buffer) void Shader::getTranslatedSourceWithDebugInfo(GLsizei bufSize, GLsizei *length, char *buffer) const { - std::string debugInfo(mImplementation->getDebugInfo()); + const std::string &debugInfo = mImplementation->getDebugInfo(); getSourceImpl(debugInfo, bufSize, length, buffer); } @@ -219,7 +230,9 @@ void Shader::compile(Compiler *compiler) std::stringstream sourceStream; - int additionalOptions = mImplementation->prepareSourceAndReturnOptions(&sourceStream); + std::string sourcePath; + int additionalOptions = + mImplementation->prepareSourceAndReturnOptions(&sourceStream, &sourcePath); int compileOptions = (SH_OBJECT_CODE | SH_VARIABLES | additionalOptions); // Some targets (eg D3D11 Feature Level 9_3 and below) do not support non-constant loop indexes @@ -231,8 +244,17 @@ void Shader::compile(Compiler *compiler) } std::string sourceString = sourceStream.str(); - const char *sourceCString = sourceString.c_str(); - bool result = ShCompile(compilerHandle, &sourceCString, 1, compileOptions); + std::vector sourceCStrings; + + if (!sourcePath.empty()) + { + sourceCStrings.push_back(sourcePath.c_str()); + } + + sourceCStrings.push_back(sourceString.c_str()); + + bool result = + ShCompile(compilerHandle, &sourceCStrings[0], sourceCStrings.size(), compileOptions); if (!result) { @@ -282,7 +304,7 @@ void Shader::compile(Compiler *compiler) ASSERT(mData.mShaderType == GL_FRAGMENT_SHADER); // TODO(jmadill): Figure out why we only sort in the FS, and if we need to. - std::sort(mData.mVaryings.begin(), mData.mVaryings.end(), CompareVarying); + std::sort(mData.mVaryings.begin(), mData.mVaryings.end(), CompareShaderVar); mData.mActiveOutputVariables = GetActiveShaderVariables(ShGetOutputVariables(compilerHandle)); } diff --git a/gfx/angle/src/libANGLE/Shader.h b/gfx/angle/src/libANGLE/Shader.h index 35086098ba0c..290e1cd6bf3e 100644 --- a/gfx/angle/src/libANGLE/Shader.h +++ b/gfx/angle/src/libANGLE/Shader.h @@ -98,6 +98,7 @@ class Shader : angle::NonCopyable int getSourceLength() const; void getSource(GLsizei bufSize, GLsizei *length, char *buffer) const; int getTranslatedSourceLength() const; + int getTranslatedSourceWithDebugInfoLength() const; const std::string &getTranslatedSource() const { return mData.getTranslatedSource(); } void getTranslatedSource(GLsizei bufSize, GLsizei *length, char *buffer) const; void getTranslatedSourceWithDebugInfo(GLsizei bufSize, GLsizei *length, char *buffer) const; @@ -137,6 +138,7 @@ class Shader : angle::NonCopyable ResourceManager *mResourceManager; }; +bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y); } #endif // LIBANGLE_SHADER_H_ diff --git a/gfx/angle/src/libANGLE/State.cpp b/gfx/angle/src/libANGLE/State.cpp index 32112a8a8d3b..5f05f25155e2 100644 --- a/gfx/angle/src/libANGLE/State.cpp +++ b/gfx/angle/src/libANGLE/State.cpp @@ -20,10 +20,28 @@ namespace gl { State::State() + : mMaxDrawBuffers(0), + mMaxCombinedTextureImageUnits(0), + mDepthClearValue(0), + mStencilClearValue(0), + mScissorTest(false), + mSampleCoverage(false), + mSampleCoverageValue(0), + mSampleCoverageInvert(false), + mStencilRef(0), + mStencilBackRef(0), + mLineWidth(0), + mGenerateMipmapHint(GL_NONE), + mFragmentShaderDerivativeHint(GL_NONE), + mNearZ(0), + mFarZ(0), + mReadFramebuffer(nullptr), + mDrawFramebuffer(nullptr), + mProgram(nullptr), + mVertexArray(nullptr), + mActiveSampler(0), + mPrimitiveRestart(false) { - mMaxDrawBuffers = 0; - mMaxCombinedTextureImageUnits = 0; - // Initialize dirty bit masks // TODO(jmadill): additional ES3 state mUnpackStateBitMask.set(DIRTY_BIT_UNPACK_ALIGNMENT); @@ -1265,6 +1283,9 @@ void State::getBooleanv(GLenum pname, GLboolean *params) case GL_DITHER: *params = mBlend.dither; break; case GL_TRANSFORM_FEEDBACK_ACTIVE: *params = getCurrentTransformFeedback()->isActive() ? GL_TRUE : GL_FALSE; break; case GL_TRANSFORM_FEEDBACK_PAUSED: *params = getCurrentTransformFeedback()->isPaused() ? GL_TRUE : GL_FALSE; break; + case GL_PRIMITIVE_RESTART_FIXED_INDEX: + *params = mPrimitiveRestart; + break; default: UNREACHABLE(); break; @@ -1504,6 +1525,9 @@ void State::getIntegerv(const gl::Data &data, GLenum pname, GLint *params) case GL_UNIFORM_BUFFER_BINDING: *params = mGenericUniformBuffer.id(); break; + case GL_TRANSFORM_FEEDBACK_BINDING: + *params = mTransformFeedback->id(); + break; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: *params = mTransformFeedback->getGenericBuffer().id(); break; diff --git a/gfx/angle/src/libANGLE/Surface_unittest.cpp b/gfx/angle/src/libANGLE/Surface_unittest.cpp index f65e100a9e5f..cfa9cda212f1 100644 --- a/gfx/angle/src/libANGLE/Surface_unittest.cpp +++ b/gfx/angle/src/libANGLE/Surface_unittest.cpp @@ -12,9 +12,11 @@ #include "libANGLE/Data.h" #include "libANGLE/State.h" #include "libANGLE/Surface.h" -#include "libANGLE/renderer/FramebufferImpl.h" +#include "libANGLE/renderer/FramebufferImpl_mock.h" #include "libANGLE/renderer/SurfaceImpl.h" +using namespace rx; + namespace { @@ -41,49 +43,6 @@ class MockSurfaceImpl : public rx::SurfaceImpl MOCK_METHOD0(destroy, void()); }; -class MockFramebufferImpl : public rx::FramebufferImpl -{ - public: - MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {} - virtual ~MockFramebufferImpl() { destroy(); } - - MOCK_METHOD1(onUpdateColorAttachment, void(size_t)); - MOCK_METHOD0(onUpdateDepthAttachment, void()); - MOCK_METHOD0(onUpdateStencilAttachment, void()); - MOCK_METHOD0(onUpdateDepthStencilAttachment, void()); - - MOCK_METHOD2(setDrawBuffers, void(size_t, const GLenum *)); - MOCK_METHOD1(setReadBuffer, void(GLenum)); - - MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *)); - MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *)); - MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &)); - - MOCK_METHOD2(clear, gl::Error(const gl::Data &, GLbitfield)); - MOCK_METHOD4(clearBufferfv, gl::Error(const gl::State &, GLenum, GLint, const GLfloat *)); - MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::State &, GLenum, GLint, const GLuint *)); - MOCK_METHOD4(clearBufferiv, gl::Error(const gl::State &, GLenum, GLint, const GLint *)); - MOCK_METHOD5(clearBufferfi, gl::Error(const gl::State &, GLenum, GLint, GLfloat, GLint)); - - MOCK_CONST_METHOD0(getImplementationColorReadFormat, GLenum()); - MOCK_CONST_METHOD0(getImplementationColorReadType, GLenum()); - MOCK_CONST_METHOD5( - readPixels, - gl::Error(const gl::State &, const gl::Rectangle &, GLenum, GLenum, GLvoid *)); - - MOCK_METHOD6(blit, - gl::Error(const gl::State &, - const gl::Rectangle &, - const gl::Rectangle &, - GLbitfield, - GLenum, - const gl::Framebuffer *)); - - MOCK_CONST_METHOD0(checkStatus, GLenum()); - - MOCK_METHOD0(destroy, void()); -}; - TEST(SurfaceTest, DestructionDeletesImpl) { MockFramebufferImpl *framebuffer = new MockFramebufferImpl; diff --git a/gfx/angle/src/libANGLE/VertexArray.cpp b/gfx/angle/src/libANGLE/VertexArray.cpp index 4afc361963c4..afa433247cb0 100644 --- a/gfx/angle/src/libANGLE/VertexArray.cpp +++ b/gfx/angle/src/libANGLE/VertexArray.cpp @@ -34,7 +34,6 @@ VertexArray::VertexArray(rx::ImplFactory *factory, GLuint id, size_t maxAttribs) mVertexArray(factory->createVertexArray(mData)), mData(maxAttribs) { - ASSERT(mVertexArray != nullptr); } VertexArray::~VertexArray() diff --git a/gfx/angle/src/libANGLE/angletypes.cpp b/gfx/angle/src/libANGLE/angletypes.cpp index c1c05edf04a2..90b6af799943 100644 --- a/gfx/angle/src/libANGLE/angletypes.cpp +++ b/gfx/angle/src/libANGLE/angletypes.cpp @@ -15,6 +15,30 @@ namespace gl { +PrimitiveType GetPrimitiveType(GLenum drawMode) +{ + switch (drawMode) + { + case GL_POINTS: + return PRIMITIVE_POINTS; + case GL_LINES: + return PRIMITIVE_LINES; + case GL_LINE_STRIP: + return PRIMITIVE_LINE_STRIP; + case GL_LINE_LOOP: + return PRIMITIVE_LINE_LOOP; + case GL_TRIANGLES: + return PRIMITIVE_TRIANGLES; + case GL_TRIANGLE_STRIP: + return PRIMITIVE_TRIANGLE_STRIP; + case GL_TRIANGLE_FAN: + return PRIMITIVE_TRIANGLE_FAN; + default: + UNREACHABLE(); + return PRIMITIVE_TYPE_MAX; + } +} + SamplerState::SamplerState() : minFilter(GL_NEAREST_MIPMAP_LINEAR), magFilter(GL_LINEAR), diff --git a/gfx/angle/src/libANGLE/angletypes.h b/gfx/angle/src/libANGLE/angletypes.h index 9fea75008134..d64a458eb4c6 100644 --- a/gfx/angle/src/libANGLE/angletypes.h +++ b/gfx/angle/src/libANGLE/angletypes.h @@ -24,6 +24,20 @@ class Program; struct VertexAttribute; struct VertexAttribCurrentValueData; +enum PrimitiveType +{ + PRIMITIVE_POINTS, + PRIMITIVE_LINES, + PRIMITIVE_LINE_STRIP, + PRIMITIVE_LINE_LOOP, + PRIMITIVE_TRIANGLES, + PRIMITIVE_TRIANGLE_STRIP, + PRIMITIVE_TRIANGLE_FAN, + PRIMITIVE_TYPE_MAX, +}; + +PrimitiveType GetPrimitiveType(GLenum drawMode); + enum SamplerType { SAMPLER_PIXEL, diff --git a/gfx/angle/src/libANGLE/features.h b/gfx/angle/src/libANGLE/features.h index 64b64690eb11..ecf486dcf7d1 100644 --- a/gfx/angle/src/libANGLE/features.h +++ b/gfx/angle/src/libANGLE/features.h @@ -32,10 +32,14 @@ #define ANGLE_PROGRAM_BINARY_LOAD ANGLE_ENABLED #endif -// Shader debug info -#if !defined(ANGLE_SHADER_DEBUG_INFO) -#define ANGLE_SHADER_DEBUG_INFO ANGLE_DISABLED -#endif +// Append HLSL assembly to shader debug info. Defaults to enabled in Debug and off in Release. +#if !defined(ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO) +#if !defined(NDEBUG) +#define ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO ANGLE_ENABLED +#else +#define ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO ANGLE_DISABLED +#endif // !defined(NDEBUG) +#endif // !defined(ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO) // Program link validation of precisions for uniforms. This feature was // requested by developers to allow non-conformant shaders to be used which diff --git a/gfx/angle/src/libANGLE/formatutils.cpp b/gfx/angle/src/libANGLE/formatutils.cpp index 0ea125e25395..949141330d46 100644 --- a/gfx/angle/src/libANGLE/formatutils.cpp +++ b/gfx/angle/src/libANGLE/formatutils.cpp @@ -212,6 +212,13 @@ static bool RequireExtAndExt(GLuint, const Extensions &extensions) return extensions.*bool1 && extensions.*bool2; } +// Check support for either of two extensions +template +static bool RequireExtOrExt(GLuint, const Extensions &extensions) +{ + return extensions.*bool1 || extensions.*bool2; +} + InternalFormat::InternalFormat() : redBits(0), greenBits(0), @@ -459,15 +466,50 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap() map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, CompressedFormat(4, 4, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_UNSIGNED_BYTE, true, RequireES<3>, NeverSupported, AlwaysSupported))); // From GL_EXT_texture_compression_dxt1 - // | Internal format | |W |H | BS |CC| Format | Type | SRGB | Supported | Renderable | Filterable | - map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGB_S3TC_DXT1_EXT, CompressedFormat(4, 4, 64, 3, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT1>, NeverSupported, AlwaysSupported))); - map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, CompressedFormat(4, 4, 64, 4, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT1>, NeverSupported, AlwaysSupported))); + // | Internal format | |W |H | BS |CC| Format | Type | SRGB | Supported | Renderable | Filterable | + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGB_S3TC_DXT1_EXT, CompressedFormat(4, 4, 64, 3, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT1>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, CompressedFormat(4, 4, 64, 4, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT1>, NeverSupported, AlwaysSupported))); // From GL_ANGLE_texture_compression_dxt3 - map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, CompressedFormat(4, 4, 128, 4, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT5>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, CompressedFormat(4, 4, 128, 4, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT5>, NeverSupported, AlwaysSupported))); // From GL_ANGLE_texture_compression_dxt5 - map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, CompressedFormat(4, 4, 128, 4, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT5>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, CompressedFormat(4, 4, 128, 4, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::textureCompressionDXT5>, NeverSupported, AlwaysSupported))); + + // From GL_OES_compressed_ETC1_RGB8_texture + map.insert(InternalFormatInfoPair(GL_ETC1_RGB8_OES, CompressedFormat(4, 4, 64, 3, GL_ETC1_RGB8_OES, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::compressedETC1RGB8Texture>, NeverSupported, AlwaysSupported))); + + // From KHR_texture_compression_astc_hdr + // | Internal format | | W | H | BS |CC| Format | Type | SRGB | Supported | Renderable | Filterable | + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, CompressedFormat( 4, 4, 128, 4, GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_5x4_KHR, CompressedFormat( 5, 4, 128, 4, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, CompressedFormat( 5, 5, 128, 4, GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_6x5_KHR, CompressedFormat( 6, 5, 128, 4, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, CompressedFormat( 6, 6, 128, 4, GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_8x5_KHR, CompressedFormat( 8, 5, 128, 4, GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_8x6_KHR, CompressedFormat( 8, 6, 128, 4, GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, CompressedFormat( 8, 8, 128, 4, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_10x5_KHR, CompressedFormat(10, 5, 128, 4, GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_10x6_KHR, CompressedFormat(10, 6, 128, 4, GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_10x8_KHR, CompressedFormat(10, 8, 128, 4, GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, CompressedFormat(10, 10, 128, 4, GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_12x10_KHR, CompressedFormat(12, 10, 128, 4, GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, CompressedFormat(12, 12, 128, 4, GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_UNSIGNED_BYTE, false, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, CompressedFormat( 4, 4, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, CompressedFormat( 5, 4, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, CompressedFormat( 5, 5, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, CompressedFormat( 6, 5, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, CompressedFormat( 6, 6, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, CompressedFormat( 8, 5, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, CompressedFormat( 8, 6, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, CompressedFormat( 8, 8, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, CompressedFormat(10, 5, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, CompressedFormat(10, 6, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, CompressedFormat(10, 8, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, CompressedFormat(10, 10, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, CompressedFormat(12, 10, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); + map.insert(InternalFormatInfoPair(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, CompressedFormat(12, 12, 128, 4, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_UNSIGNED_BYTE, true, RequireExtOrExt<&Extensions::textureCompressionASTCHDR, &Extensions::textureCompressionASTCLDR>, NeverSupported, AlwaysSupported))); // For STENCIL_INDEX8 we chose a normalized component type for the following reasons: // - Multisampled buffer are disallowed for non-normalized integer component types and we want to support it for STENCIL_INDEX8 diff --git a/gfx/angle/src/libANGLE/formatutils.h b/gfx/angle/src/libANGLE/formatutils.h index 4ac12658a770..6863e4ddc42d 100644 --- a/gfx/angle/src/libANGLE/formatutils.h +++ b/gfx/angle/src/libANGLE/formatutils.h @@ -229,6 +229,6 @@ VertexFormatType GetVertexFormatType(const VertexAttribute &attrib); VertexFormatType GetVertexFormatType(const VertexAttribute &attrib, GLenum currentValueType); const VertexFormat &GetVertexFormatFromType(VertexFormatType vertexFormatType); -} +} // namespace gl #endif // LIBANGLE_FORMATUTILS_H_ diff --git a/gfx/angle/src/libANGLE/moz.build b/gfx/angle/src/libANGLE/moz.build index c60296945021..32d1071c263f 100644 --- a/gfx/angle/src/libANGLE/moz.build +++ b/gfx/angle/src/libANGLE/moz.build @@ -47,6 +47,7 @@ UNIFIED_SOURCES += [ '../compiler/translator/Diagnostics.cpp', '../compiler/translator/DirectiveHandler.cpp', '../compiler/translator/EmulatePrecision.cpp', + '../compiler/translator/ExtensionGLSL.cpp', '../compiler/translator/FlagStd140Structs.cpp', '../compiler/translator/ForLoopUnroll.cpp', '../compiler/translator/InfoSink.cpp', @@ -64,7 +65,6 @@ UNIFIED_SOURCES += [ '../compiler/translator/OutputGLSL.cpp', '../compiler/translator/OutputGLSLBase.cpp', '../compiler/translator/OutputHLSL.cpp', - '../compiler/translator/parseConst.cpp', '../compiler/translator/ParseContext.cpp', '../compiler/translator/PoolAlloc.cpp', '../compiler/translator/PruneEmptyDeclarations.cpp', @@ -159,6 +159,7 @@ UNIFIED_SOURCES += [ 'renderer/d3d/IndexBuffer.cpp', 'renderer/d3d/IndexDataManager.cpp', 'renderer/d3d/loadimage.cpp', + 'renderer/d3d/loadimage_etc.cpp', 'renderer/d3d/ProgramD3D.cpp', 'renderer/d3d/RenderbufferD3D.cpp', 'renderer/d3d/RendererD3D.cpp', @@ -168,6 +169,7 @@ UNIFIED_SOURCES += [ 'renderer/d3d/SurfaceD3D.cpp', 'renderer/d3d/TextureD3D.cpp', 'renderer/d3d/TransformFeedbackD3D.cpp', + 'renderer/d3d/VaryingPacking.cpp', 'renderer/d3d/VertexBuffer.cpp', 'renderer/d3d/VertexDataManager.cpp', 'renderer/DeviceImpl.cpp', @@ -236,6 +238,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: 'renderer/d3d/d3d11/Image11.cpp', 'renderer/d3d/d3d11/IndexBuffer11.cpp', 'renderer/d3d/d3d11/InputLayoutCache.cpp', + 'renderer/d3d/d3d11/internal_format_initializer_table.cpp', + 'renderer/d3d/d3d11/load_functions_table_autogen.cpp', 'renderer/d3d/d3d11/PixelTransfer11.cpp', 'renderer/d3d/d3d11/Query11.cpp', 'renderer/d3d/d3d11/Renderer11.cpp', @@ -243,9 +247,9 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: 'renderer/d3d/d3d11/RenderStateCache.cpp', 'renderer/d3d/d3d11/RenderTarget11.cpp', 'renderer/d3d/d3d11/ShaderExecutable11.cpp', - 'renderer/d3d/d3d11/swizzle_format_info.cpp', - 'renderer/d3d/d3d11/texture_format_table.cpp', - 'renderer/d3d/d3d11/texture_format_util.cpp', + 'renderer/d3d/d3d11/StateManager11.cpp', + 'renderer/d3d/d3d11/swizzle_format_info_autogen.cpp', + 'renderer/d3d/d3d11/texture_format_table_autogen.cpp', 'renderer/d3d/d3d11/TextureStorage11.cpp', 'renderer/d3d/d3d11/Trim11.cpp', 'renderer/d3d/d3d11/VertexBuffer11.cpp', @@ -310,8 +314,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: OS_LIBS += [ 'd3d9', 'dxguid' ] else: EXTRA_DSO_LDOPTS += [ - '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), - '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), ] Library('libANGLE') diff --git a/gfx/angle/src/libANGLE/queryconversions.cpp b/gfx/angle/src/libANGLE/queryconversions.cpp index 5633ce4d2ea8..3a6059a89cb5 100644 --- a/gfx/angle/src/libANGLE/queryconversions.cpp +++ b/gfx/angle/src/libANGLE/queryconversions.cpp @@ -8,6 +8,8 @@ #include "libANGLE/queryconversions.h" +#include + #include "libANGLE/Context.h" #include "common/utilities.h" @@ -101,59 +103,43 @@ void CastStateValues(Context *context, GLenum nativeType, GLenum pname, { if (nativeType == GL_INT) { - GLint *intParams = NULL; - intParams = new GLint[numParams]; - - context->getIntegerv(pname, intParams); + std::vector intParams(numParams, 0); + context->getIntegerv(pname, intParams.data()); for (unsigned int i = 0; i < numParams; ++i) { outParams[i] = CastStateValue(pname, intParams[i]); } - - delete [] intParams; } else if (nativeType == GL_BOOL) { - GLboolean *boolParams = NULL; - boolParams = new GLboolean[numParams]; - - context->getBooleanv(pname, boolParams); + std::vector boolParams(numParams, GL_FALSE); + context->getBooleanv(pname, boolParams.data()); for (unsigned int i = 0; i < numParams; ++i) { outParams[i] = (boolParams[i] == GL_FALSE ? static_cast(0) : static_cast(1)); } - - delete [] boolParams; } else if (nativeType == GL_FLOAT) { - GLfloat *floatParams = NULL; - floatParams = new GLfloat[numParams]; - - context->getFloatv(pname, floatParams); + std::vector floatParams(numParams, 0.0f); + context->getFloatv(pname, floatParams.data()); for (unsigned int i = 0; i < numParams; ++i) { outParams[i] = CastStateValue(pname, floatParams[i]); } - - delete [] floatParams; } else if (nativeType == GL_INT_64_ANGLEX) { - GLint64 *int64Params = NULL; - int64Params = new GLint64[numParams]; - - context->getInteger64v(pname, int64Params); + std::vector int64Params(numParams, 0); + context->getInteger64v(pname, int64Params.data()); for (unsigned int i = 0; i < numParams; ++i) { outParams[i] = CastStateValue(pname, int64Params[i]); } - - delete [] int64Params; } else UNREACHABLE(); } diff --git a/gfx/angle/src/libANGLE/renderer/DeviceImpl.h b/gfx/angle/src/libANGLE/renderer/DeviceImpl.h index eb150549e144..99793f56866b 100644 --- a/gfx/angle/src/libANGLE/renderer/DeviceImpl.h +++ b/gfx/angle/src/libANGLE/renderer/DeviceImpl.h @@ -26,7 +26,7 @@ class DeviceImpl : angle::NonCopyable DeviceImpl(); virtual ~DeviceImpl(); - virtual egl::Error getDevice(EGLAttrib *value) = 0; + virtual egl::Error getDevice(void **outValue) = 0; virtual EGLint getType() = 0; virtual void generateExtensions(egl::DeviceExtensions *outExtensions) const = 0; }; diff --git a/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h b/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h new file mode 100644 index 000000000000..d5a0dc677eda --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h @@ -0,0 +1,65 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// FramebufferImpl_mock.h: +// Defines a mock of the FramebufferImpl class. +// + +#ifndef LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_ +#define LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_ + +#include "gmock/gmock.h" + +#include "libANGLE/renderer/FramebufferImpl.h" + +namespace rx +{ + +class MockFramebufferImpl : public rx::FramebufferImpl +{ + public: + MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {} + virtual ~MockFramebufferImpl() { destroy(); } + + MOCK_METHOD1(onUpdateColorAttachment, void(size_t)); + MOCK_METHOD0(onUpdateDepthAttachment, void()); + MOCK_METHOD0(onUpdateStencilAttachment, void()); + MOCK_METHOD0(onUpdateDepthStencilAttachment, void()); + + MOCK_METHOD2(setDrawBuffers, void(size_t, const GLenum *)); + MOCK_METHOD1(setReadBuffer, void(GLenum)); + + MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *)); + MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *)); + MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &)); + + MOCK_METHOD2(clear, gl::Error(const gl::Data &, GLbitfield)); + MOCK_METHOD4(clearBufferfv, gl::Error(const gl::State &, GLenum, GLint, const GLfloat *)); + MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::State &, GLenum, GLint, const GLuint *)); + MOCK_METHOD4(clearBufferiv, gl::Error(const gl::State &, GLenum, GLint, const GLint *)); + MOCK_METHOD5(clearBufferfi, gl::Error(const gl::State &, GLenum, GLint, GLfloat, GLint)); + + MOCK_CONST_METHOD0(getImplementationColorReadFormat, GLenum()); + MOCK_CONST_METHOD0(getImplementationColorReadType, GLenum()); + MOCK_CONST_METHOD5( + readPixels, + gl::Error(const gl::State &, const gl::Rectangle &, GLenum, GLenum, GLvoid *)); + + MOCK_METHOD6(blit, + gl::Error(const gl::State &, + const gl::Rectangle &, + const gl::Rectangle &, + GLbitfield, + GLenum, + const gl::Framebuffer *)); + + MOCK_CONST_METHOD0(checkStatus, GLenum()); + + MOCK_METHOD0(destroy, void()); +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_ diff --git a/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h b/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h new file mode 100644 index 000000000000..dbc15345f02f --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h @@ -0,0 +1,64 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ProgramImpl_mock.h: +// Defines a mock of the ProgramImpl class. +// + +#ifndef LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_ +#define LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_ + +#include "gmock/gmock.h" + +#include "libANGLE/renderer/ProgramImpl.h" + +namespace rx +{ + +class MockProgramImpl : public rx::ProgramImpl +{ + public: + MockProgramImpl() : ProgramImpl(gl::Program::Data()) {} + virtual ~MockProgramImpl() { destroy(); } + + MOCK_METHOD2(load, LinkResult(gl::InfoLog &, gl::BinaryInputStream *)); + MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *)); + + MOCK_METHOD2(link, LinkResult(const gl::Data &, gl::InfoLog &)); + MOCK_METHOD2(validate, GLboolean(const gl::Caps &, gl::InfoLog *)); + + MOCK_METHOD3(setUniform1fv, void(GLint, GLsizei, const GLfloat *)); + MOCK_METHOD3(setUniform2fv, void(GLint, GLsizei, const GLfloat *)); + MOCK_METHOD3(setUniform3fv, void(GLint, GLsizei, const GLfloat *)); + MOCK_METHOD3(setUniform4fv, void(GLint, GLsizei, const GLfloat *)); + MOCK_METHOD3(setUniform1iv, void(GLint, GLsizei, const GLint *)); + MOCK_METHOD3(setUniform2iv, void(GLint, GLsizei, const GLint *)); + MOCK_METHOD3(setUniform3iv, void(GLint, GLsizei, const GLint *)); + MOCK_METHOD3(setUniform4iv, void(GLint, GLsizei, const GLint *)); + MOCK_METHOD3(setUniform1uiv, void(GLint, GLsizei, const GLuint *)); + MOCK_METHOD3(setUniform2uiv, void(GLint, GLsizei, const GLuint *)); + MOCK_METHOD3(setUniform3uiv, void(GLint, GLsizei, const GLuint *)); + MOCK_METHOD3(setUniform4uiv, void(GLint, GLsizei, const GLuint *)); + + MOCK_METHOD4(setUniformMatrix2fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix3fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix4fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix2x3fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix3x2fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix2x4fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix4x2fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix3x4fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + MOCK_METHOD4(setUniformMatrix4x3fv, void(GLint, GLsizei, GLboolean, const GLfloat *)); + + MOCK_METHOD2(setUniformBlockBinding, void(GLuint, GLuint)); + MOCK_CONST_METHOD2(getUniformBlockSize, bool(const std::string &, size_t *)); + MOCK_CONST_METHOD2(getUniformBlockMemberInfo, bool(const std::string &, sh::BlockMemberInfo *)); + + MOCK_METHOD0(destroy, void()); +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_ diff --git a/gfx/angle/src/libANGLE/renderer/ShaderImpl.h b/gfx/angle/src/libANGLE/renderer/ShaderImpl.h index ec164f3f7fc9..5a466377a53a 100644 --- a/gfx/angle/src/libANGLE/renderer/ShaderImpl.h +++ b/gfx/angle/src/libANGLE/renderer/ShaderImpl.h @@ -22,12 +22,15 @@ class ShaderImpl : angle::NonCopyable virtual ~ShaderImpl() { } // Returns additional ShCompile options. - virtual int prepareSourceAndReturnOptions(std::stringstream *sourceStream) = 0; + virtual int prepareSourceAndReturnOptions(std::stringstream *sourceStream, + std::string *sourcePath) = 0; // Returns success for compiling on the driver. Returns success. virtual bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) = 0; virtual std::string getDebugInfo() const = 0; + const gl::Shader::Data &getData() const { return mData; } + protected: const gl::Shader::Data &mData; }; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp index 693b27116fa8..91dddc505c51 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp @@ -8,6 +8,7 @@ #include "libANGLE/renderer/d3d/BufferD3D.h" +#include "common/mathutil.h" #include "common/utilities.h" #include "libANGLE/renderer/d3d/IndexBuffer.h" #include "libANGLE/renderer/d3d/VertexBuffer.h" @@ -22,6 +23,9 @@ BufferD3D::BufferD3D(BufferFactoryD3D *factory) mFactory(factory), mStaticVertexBuffer(nullptr), mStaticIndexBuffer(nullptr), + mStaticBufferCache(nullptr), + mStaticBufferCacheTotalSize(0), + mStaticVertexBufferOutOfDate(false), mUnmodifiedDataUse(0), mUsage(D3D_BUFFER_USAGE_STATIC) { @@ -32,6 +36,14 @@ BufferD3D::~BufferD3D() { SafeDelete(mStaticVertexBuffer); SafeDelete(mStaticIndexBuffer); + + // Empty the cache of static vertex buffers too + if (mStaticBufferCache != nullptr) + { + SafeDeleteContainer(*mStaticBufferCache); + } + + mStaticBufferCacheTotalSize = 0; } void BufferD3D::updateSerial() @@ -75,8 +87,130 @@ void BufferD3D::initializeStaticData() } } -void BufferD3D::invalidateStaticData() +StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer() { + return mStaticIndexBuffer; +} + +StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer( + const gl::VertexAttribute &attribute, + D3DStaticBufferCreationType creationType) +{ + if (!mStaticVertexBuffer) + { + // Early out if there aren't any static buffers at all + ASSERT(mStaticBufferCache == nullptr); + return nullptr; + } + + if (mStaticBufferCache == nullptr && !mStaticVertexBuffer->isCommitted()) + { + // Early out, the attribute can be added to mStaticVertexBuffer or is already in there + return mStaticVertexBuffer; + } + + // At this point, see if any of the existing static buffers contains the attribute data + + // If the default static vertex buffer contains the attribute, then return it + if (mStaticVertexBuffer->lookupAttribute(attribute, nullptr)) + { + return mStaticVertexBuffer; + } + + if (mStaticBufferCache != nullptr) + { + // If there is a cached static buffer that already contains the attribute, then return it + for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache) + { + if (staticBuffer->lookupAttribute(attribute, nullptr)) + { + return staticBuffer; + } + } + } + + if (!mStaticVertexBuffer->isCommitted()) + { + // None of the existing static buffers contain the attribute data and we are able to add + // the data to mStaticVertexBuffer, so we should just do so + return mStaticVertexBuffer; + } + + // At this point, we must create a new static buffer for the attribute data + if (creationType != D3D_BUFFER_CREATE_IF_NECESSARY) + { + return nullptr; + } + + ASSERT(mStaticVertexBuffer); + ASSERT(mStaticVertexBuffer->isCommitted()); + unsigned int staticVertexBufferSize = mStaticVertexBuffer->getBufferSize(); + if (IsUnsignedAdditionSafe(staticVertexBufferSize, mStaticBufferCacheTotalSize)) + { + // Ensure that the total size of the static buffer cache remains less than 4x the + // size of the original buffer + unsigned int maxStaticCacheSize = + IsUnsignedMultiplicationSafe(static_cast(getSize()), 4u) + ? 4u * static_cast(getSize()) + : std::numeric_limits::max(); + + // We can't reuse the default static vertex buffer, so we add it to the cache + if (staticVertexBufferSize + mStaticBufferCacheTotalSize <= maxStaticCacheSize) + { + if (mStaticBufferCache == nullptr) + { + mStaticBufferCache = new std::vector(); + } + + mStaticBufferCacheTotalSize += staticVertexBufferSize; + (*mStaticBufferCache).push_back(mStaticVertexBuffer); + mStaticVertexBuffer = nullptr; + + // Then reinitialize the static buffers to create a new static vertex buffer + initializeStaticData(); + + // Return the default static vertex buffer + return mStaticVertexBuffer; + } + } + + // At this point: + // - mStaticVertexBuffer is committed and can't be altered + // - mStaticBufferCache is full (or nearly overflowing) + // The inputted attribute should be put in some static buffer at some point, but it can't + // go in one right now, since mStaticBufferCache is full and we can't delete mStaticVertexBuffer + // in case another attribute is relying upon it for the current draw. + // We therefore mark mStaticVertexBuffer for deletion at the next possible time. + mStaticVertexBufferOutOfDate = true; + return nullptr; +} + +void BufferD3D::reinitOutOfDateStaticData() +{ + if (mStaticVertexBufferOutOfDate) + { + // During the last draw the caller tried to use some attribute with static data, but neither + // the static buffer cache nor mStaticVertexBuffer contained that data. + // Therefore, invalidate mStaticVertexBuffer so that if the caller tries to use that + // attribute in the next draw, it'll successfully get put into mStaticVertexBuffer. + invalidateStaticData(D3D_BUFFER_INVALIDATE_DEFAULT_BUFFER_ONLY); + mStaticVertexBufferOutOfDate = false; + } +} + +void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType) +{ + if (invalidationType == D3D_BUFFER_INVALIDATE_WHOLE_CACHE && mStaticBufferCache != nullptr) + { + // Empty the cache of static vertex buffers too + for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache) + { + SafeDelete(staticBuffer); + } + mStaticBufferCache->clear(); + mStaticBufferCacheTotalSize = 0; + } + if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0)) { SafeDelete(mStaticVertexBuffer); @@ -98,6 +232,10 @@ void BufferD3D::promoteStaticUsage(int dataSize) { if (!mStaticVertexBuffer && !mStaticIndexBuffer) { + // There isn't any scenario that involves promoting static usage and the static buffer cache + // being non-empty + ASSERT(mStaticBufferCache == nullptr); + mUnmodifiedDataUse += dataSize; if (mUnmodifiedDataUse > 3 * getSize()) diff --git a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h index 239b7fb9f0ce..849064ced1ba 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h @@ -13,6 +13,7 @@ #include "libANGLE/renderer/BufferImpl.h" #include +#include namespace rx { @@ -26,6 +27,18 @@ enum D3DBufferUsage D3D_BUFFER_USAGE_DYNAMIC, }; +enum D3DBufferInvalidationType +{ + D3D_BUFFER_INVALIDATE_WHOLE_CACHE, + D3D_BUFFER_INVALIDATE_DEFAULT_BUFFER_ONLY, +}; + +enum D3DStaticBufferCreationType +{ + D3D_BUFFER_CREATE_IF_NECESSARY, + D3D_BUFFER_DO_NOT_CREATE, +}; + class BufferD3D : public BufferImpl { public: @@ -39,11 +52,14 @@ class BufferD3D : public BufferImpl virtual void markTransformFeedbackUsage() = 0; virtual gl::Error getData(const uint8_t **outData) = 0; - StaticVertexBufferInterface *getStaticVertexBuffer() { return mStaticVertexBuffer; } - StaticIndexBufferInterface *getStaticIndexBuffer() { return mStaticIndexBuffer; } + StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute, + D3DStaticBufferCreationType creationType); + StaticIndexBufferInterface *getStaticIndexBuffer(); void initializeStaticData(); - void invalidateStaticData(); + void invalidateStaticData(D3DBufferInvalidationType invalidationType); + void reinitOutOfDateStaticData(); + void promoteStaticUsage(int dataSize); gl::Error getIndexRange(GLenum type, @@ -62,6 +78,9 @@ class BufferD3D : public BufferImpl StaticVertexBufferInterface *mStaticVertexBuffer; StaticIndexBufferInterface *mStaticIndexBuffer; + std::vector *mStaticBufferCache; + unsigned int mStaticBufferCacheTotalSize; + unsigned int mStaticVertexBufferOutOfDate; unsigned int mUnmodifiedDataUse; D3DBufferUsage mUsage; }; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp index 938703ac151e..1608228bc717 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp @@ -17,33 +17,19 @@ namespace rx { -DeviceD3D::DeviceD3D(rx::RendererD3D *renderer) - : mRenderer(renderer) +DeviceD3D::DeviceD3D(void *device, EGLint deviceType) : mDevice(device), mDeviceType(deviceType) { } -egl::Error DeviceD3D::getDevice(EGLAttrib *value) +egl::Error DeviceD3D::getDevice(void **outValue) { - *value = reinterpret_cast(mRenderer->getD3DDevice()); - if (*value == 0) - { - return egl::Error(EGL_BAD_DEVICE_EXT); - } + *outValue = mDevice; return egl::Error(EGL_SUCCESS); } EGLint DeviceD3D::getType() { - switch (mRenderer->getRendererClass()) - { - case RENDERER_D3D11: - return EGL_D3D11_DEVICE_ANGLE; - case RENDERER_D3D9: - return EGL_D3D9_DEVICE_ANGLE; - default: - UNREACHABLE(); - return EGL_NONE; - } + return mDeviceType; } void DeviceD3D::generateExtensions(egl::DeviceExtensions *outExtensions) const diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h index 5f9854634525..20276a7acaf9 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h @@ -18,14 +18,15 @@ namespace rx class DeviceD3D : public DeviceImpl { public: - DeviceD3D(RendererD3D *renderer); + DeviceD3D(void *device, EGLint deviceType); - egl::Error getDevice(EGLAttrib *value) override; + egl::Error getDevice(void **outValue) override; EGLint getType() override; void generateExtensions(egl::DeviceExtensions *outExtensions) const override; private: - RendererD3D *mRenderer; + void *mDevice; + EGLint mDeviceType; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp index 46d08a3d4d70..9daeb7a82840 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp @@ -141,9 +141,7 @@ egl::Error CreateRendererD3D(egl::Display *display, RendererD3D **outRenderer) return result; } -DisplayD3D::DisplayD3D() - : mRenderer(nullptr), - mDevice(nullptr) +DisplayD3D::DisplayD3D() : mRenderer(nullptr) { } @@ -209,9 +207,7 @@ ImageImpl *DisplayD3D::createImage(EGLenum target, egl::Error DisplayD3D::getDevice(DeviceImpl **device) { - *device = reinterpret_cast(mDevice); - ASSERT(*device != nullptr); - return egl::Error(EGL_SUCCESS); + return mRenderer->getEGLDevice(device); } egl::Error DisplayD3D::createContext(const egl::Config *config, const gl::Context *shareContext, const egl::AttributeMap &attribs, @@ -242,15 +238,11 @@ egl::Error DisplayD3D::initialize(egl::Display *display) return error; } - ASSERT(mDevice == nullptr); - mDevice = new DeviceD3D(mRenderer); - return egl::Error(EGL_SUCCESS); } void DisplayD3D::terminate() { - SafeDelete(mDevice); SafeDelete(mRenderer); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h index 973b77aa1a54..7ce78daea5b7 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.h @@ -65,8 +65,6 @@ class DisplayD3D : public DisplayImpl egl::Display *mDisplay; rx::RendererD3D *mRenderer; - - DeviceImpl *mDevice; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp index 6e386f937c23..1d07b95c3334 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp @@ -13,11 +13,10 @@ #include "libANGLE/Program.h" #include "libANGLE/Shader.h" #include "libANGLE/formatutils.h" +#include "libANGLE/renderer/d3d/ProgramD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/ShaderD3D.h" - -// For use with ArrayString, see angleutils.h -static_assert(GL_INVALID_INDEX == UINT_MAX, "GL_INVALID_INDEX must be equal to the max unsigned int."); +#include "libANGLE/renderer/d3d/VaryingPacking.h" using namespace gl; @@ -31,12 +30,17 @@ std::string HLSLComponentTypeString(GLenum componentType) { switch (componentType) { - case GL_UNSIGNED_INT: return "uint"; - case GL_INT: return "int"; - case GL_UNSIGNED_NORMALIZED: - case GL_SIGNED_NORMALIZED: - case GL_FLOAT: return "float"; - default: UNREACHABLE(); return "not-component-type"; + case GL_UNSIGNED_INT: + return "uint"; + case GL_INT: + return "int"; + case GL_UNSIGNED_NORMALIZED: + case GL_SIGNED_NORMALIZED: + case GL_FLOAT: + return "float"; + default: + UNREACHABLE(); + return "not-component-type"; } } @@ -49,16 +53,27 @@ std::string HLSLMatrixTypeString(GLenum type) { switch (type) { - case GL_FLOAT_MAT2: return "float2x2"; - case GL_FLOAT_MAT3: return "float3x3"; - case GL_FLOAT_MAT4: return "float4x4"; - case GL_FLOAT_MAT2x3: return "float2x3"; - case GL_FLOAT_MAT3x2: return "float3x2"; - case GL_FLOAT_MAT2x4: return "float2x4"; - case GL_FLOAT_MAT4x2: return "float4x2"; - case GL_FLOAT_MAT3x4: return "float3x4"; - case GL_FLOAT_MAT4x3: return "float4x3"; - default: UNREACHABLE(); return "not-matrix-type"; + case GL_FLOAT_MAT2: + return "float2x2"; + case GL_FLOAT_MAT3: + return "float3x3"; + case GL_FLOAT_MAT4: + return "float4x4"; + case GL_FLOAT_MAT2x3: + return "float2x3"; + case GL_FLOAT_MAT3x2: + return "float3x2"; + case GL_FLOAT_MAT2x4: + return "float2x4"; + case GL_FLOAT_MAT4x2: + return "float4x2"; + case GL_FLOAT_MAT3x4: + return "float3x4"; + case GL_FLOAT_MAT4x3: + return "float4x3"; + default: + UNREACHABLE(); + return "not-matrix-type"; } } @@ -69,11 +84,13 @@ std::string HLSLTypeString(GLenum type) return HLSLMatrixTypeString(type); } - return HLSLComponentTypeString(gl::VariableComponentType(type), gl::VariableComponentCount(type)); + return HLSLComponentTypeString(gl::VariableComponentType(type), + gl::VariableComponentCount(type)); } -const PixelShaderOutputVariable *FindOutputAtLocation(const std::vector &outputVariables, - unsigned int location) +const PixelShaderOutputVariable *FindOutputAtLocation( + const std::vector &outputVariables, + unsigned int location) { for (size_t variableIndex = 0; variableIndex < outputVariables.size(); ++variableIndex) { @@ -83,329 +100,102 @@ const PixelShaderOutputVariable *FindOutputAtLocation(const std::vectorvarying; - - if (varying.isStruct()) + static_assert(GL_INVALID_INDEX == UINT_MAX, + "GL_INVALID_INDEX must be equal to the max unsigned int."); + if (i == UINT_MAX) { - registers = HLSLVariableRegisterCount(varying, true) * varying.elementCount(); - elements = 4; - } - else - { - GLenum transposedType = TransposeMatrixType(varying.type); - registers = VariableRowCount(transposedType) * varying.elementCount(); - elements = VariableColumnCount(transposedType); + return; } - if (elements >= 2 && elements <= 4) - { - for (int r = 0; r <= maxVaryingVectors - registers; r++) - { - bool available = true; - - for (int y = 0; y < registers && available; y++) - { - for (int x = 0; x < elements && available; x++) - { - if (packing[r + y][x]) - { - available = false; - } - } - } - - if (available) - { - packedVarying->registerIndex = r; - packedVarying->columnIndex = 0; - - for (int y = 0; y < registers; y++) - { - for (int x = 0; x < elements; x++) - { - packing[r + y][x] = packedVarying; - } - } - - return true; - } - } - - if (elements == 2) - { - for (int r = maxVaryingVectors - registers; r >= 0; r--) - { - bool available = true; - - for (int y = 0; y < registers && available; y++) - { - for (int x = 2; x < 4 && available; x++) - { - if (packing[r + y][x]) - { - available = false; - } - } - } - - if (available) - { - packedVarying->registerIndex = r; - packedVarying->columnIndex = 2; - - for (int y = 0; y < registers; y++) - { - for (int x = 2; x < 4; x++) - { - packing[r + y][x] = packedVarying; - } - } - - return true; - } - } - } - } - else if (elements == 1) - { - int space[4] = { 0 }; - - for (int y = 0; y < maxVaryingVectors; y++) - { - for (int x = 0; x < 4; x++) - { - space[x] += packing[y][x] ? 0 : 1; - } - } - - int column = 0; - - for (int x = 0; x < 4; x++) - { - if (space[x] >= registers && (space[column] < registers || space[x] < space[column])) - { - column = x; - } - } - - if (space[column] >= registers) - { - for (int r = 0; r < maxVaryingVectors; r++) - { - if (!packing[r][column]) - { - packedVarying->registerIndex = r; - packedVarying->columnIndex = column; - - for (int y = r; y < r + registers; y++) - { - packing[y][column] = packedVarying; - } - - break; - } - } - - return true; - } - } - else UNREACHABLE(); - - return false; + strstr << "["; + strstr << i; + strstr << "]"; } const std::string VERTEX_ATTRIBUTE_STUB_STRING = "@@ VERTEX ATTRIBUTES @@"; const std::string PIXEL_OUTPUT_STUB_STRING = "@@ PIXEL OUTPUT @@"; +} // anonymous namespace + +std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize) +{ + // SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord) + // In D3D11 we manually compute gl_PointCoord in the GS. + return ((programUsesPointSize && majorShaderModel < 4) ? "COLOR" : "TEXCOORD"); } +// DynamicHLSL implementation + DynamicHLSL::DynamicHLSL(RendererD3D *const renderer) : mRenderer(renderer) { } -// Packs varyings into generic varying registers, using the algorithm from [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111 -// Returns the number of used varying registers, or -1 if unsuccesful -int DynamicHLSL::packVaryings(InfoLog &infoLog, - std::vector *packedVaryings, - const std::vector &transformFeedbackVaryings) +void DynamicHLSL::generateVaryingHLSL(const VaryingPacking &varyingPacking, + std::stringstream &hlslStream) const { - // TODO (geofflang): Use context's caps - const int maxVaryingVectors = mRenderer->getRendererCaps().maxVaryingVectors; + std::string varyingSemantic = + GetVaryingSemantic(mRenderer->getMajorShaderModel(), varyingPacking.usesPointSize()); - VaryingPacking packing = {}; - - std::set uniqueVaryingNames; - - for (PackedVarying &packedVarying : *packedVaryings) + for (const PackedVaryingRegister ®isterInfo : varyingPacking.getRegisterList()) { - const sh::Varying &varying = *packedVarying.varying; + const auto &varying = *registerInfo.packedVarying->varying; + ASSERT(!varying.isStruct()); - // Do not assign registers to built-in or unreferenced varyings - if (varying.isBuiltIn() || !varying.staticUse) + // TODO: Add checks to ensure D3D interpolation modifiers don't result in too many + // registers being used. + // For example, if there are N registers, and we have N vec3 varyings and 1 float + // varying, then D3D will pack them into N registers. + // If the float varying has the 'nointerpolation' modifier on it then we would need + // N + 1 registers, and D3D compilation will fail. + + switch (registerInfo.packedVarying->interpolation) { - continue; + case sh::INTERPOLATION_SMOOTH: + hlslStream << " "; + break; + case sh::INTERPOLATION_FLAT: + hlslStream << " nointerpolation "; + break; + case sh::INTERPOLATION_CENTROID: + hlslStream << " centroid "; + break; + default: + UNREACHABLE(); } - ASSERT(uniqueVaryingNames.count(varying.name) == 0); - - if (PackVarying(&packedVarying, maxVaryingVectors, packing)) - { - uniqueVaryingNames.insert(varying.name); - } - else - { - infoLog << "Could not pack varying " << varying.name; - return -1; - } + GLenum transposedType = gl::TransposeMatrixType(varying.type); + GLenum componentType = gl::VariableComponentType(transposedType); + int columnCount = gl::VariableColumnCount(transposedType); + hlslStream << HLSLComponentTypeString(componentType, columnCount); + unsigned int semanticIndex = registerInfo.semanticIndex; + hlslStream << " v" << semanticIndex << " : " << varyingSemantic << semanticIndex << ";\n"; } - - for (const std::string &transformFeedbackVaryingName : transformFeedbackVaryings) - { - if (transformFeedbackVaryingName == "gl_Position" || - transformFeedbackVaryingName == "gl_PointSize") - { - // do not pack builtin XFB varyings - continue; - } - - if (uniqueVaryingNames.count(transformFeedbackVaryingName) == 0) - { - bool found = false; - for (PackedVarying &packedVarying : *packedVaryings) - { - const sh::Varying &varying = *packedVarying.varying; - if (transformFeedbackVaryingName == varying.name) - { - if (!PackVarying(&packedVarying, maxVaryingVectors, packing)) - { - infoLog << "Could not pack varying " << varying.name; - return -1; - } - - found = true; - break; - } - } - - if (!found) - { - infoLog << "Transform feedback varying " << transformFeedbackVaryingName - << " does not exist in the vertex shader."; - return -1; - } - } - } - - // Return the number of used registers - int registers = 0; - - for (int r = 0; r < maxVaryingVectors; r++) - { - if (packing[r][0] || packing[r][1] || packing[r][2] || packing[r][3]) - { - registers++; - } - } - - return registers; } -std::string DynamicHLSL::generateVaryingHLSL(const std::vector &varyings, - bool shaderUsesPointSize) const +std::string DynamicHLSL::generateVertexShaderForInputLayout( + const std::string &sourceShader, + const InputLayout &inputLayout, + const std::vector &shaderAttributes) const { - std::string varyingSemantic = getVaryingSemantic(shaderUsesPointSize); - std::string varyingHLSL; + std::stringstream structStream; + std::stringstream initStream; - for (const PackedVarying &packedVarying : varyings) - { - if (!packedVarying.registerAssigned()) - { - continue; - } + structStream << "struct VS_INPUT\n" + << "{\n"; - const sh::Varying &varying = *packedVarying.varying; - - ASSERT(!varying.isBuiltIn()); - GLenum transposedType = TransposeMatrixType(varying.type); - int variableRows = (varying.isStruct() ? 1 : VariableRowCount(transposedType)); - - for (unsigned int elementIndex = 0; elementIndex < varying.elementCount(); elementIndex++) - { - for (int row = 0; row < variableRows; row++) - { - // TODO: Add checks to ensure D3D interpolation modifiers don't result in too many - // registers being used. - // For example, if there are N registers, and we have N vec3 varyings and 1 float - // varying, then D3D will pack them into N registers. - // If the float varying has the 'nointerpolation' modifier on it then we would need - // N + 1 registers, and D3D compilation will fail. - - switch (varying.interpolation) - { - case sh::INTERPOLATION_SMOOTH: - varyingHLSL += " "; - break; - case sh::INTERPOLATION_FLAT: - varyingHLSL += " nointerpolation "; - break; - case sh::INTERPOLATION_CENTROID: - varyingHLSL += " centroid "; - break; - default: - UNREACHABLE(); - } - - unsigned int semanticIndex = - elementIndex * variableRows + - packedVarying.columnIndex * mRenderer->getRendererCaps().maxVaryingVectors + - packedVarying.registerIndex + row; - std::string n = Str(semanticIndex); - - std::string typeString; - - if (varying.isStruct()) - { - // TODO(jmadill): pass back translated name from the shader translator - typeString = decorateVariable(varying.structName); - } - else - { - GLenum componentType = VariableComponentType(transposedType); - int columnCount = VariableColumnCount(transposedType); - typeString = HLSLComponentTypeString(componentType, columnCount); - } - varyingHLSL += typeString + " v" + n + " : " + varyingSemantic + n + ";\n"; - } - } - } - - return varyingHLSL; -} - -std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &sourceShader, - const InputLayout &inputLayout, - const std::vector &shaderAttributes) const -{ - std::string structHLSL, initHLSL; - - int semanticIndex = 0; + int semanticIndex = 0; unsigned int inputIndex = 0; // If gl_PointSize is used in the shader then pointsprites rendering is expected. // If the renderer does not support Geometry shaders then Instanced PointSprite emulation // must be used. bool usesPointSize = sourceShader.find("GL_USES_POINT_SIZE") != std::string::npos; - bool useInstancedPointSpriteEmulation = usesPointSize && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; + bool useInstancedPointSpriteEmulation = + usesPointSize && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; // Instanced PointSprite emulation requires additional entries in the // VS_INPUT structure to support the vertices that make up the quad vertices. @@ -417,8 +207,8 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s // before per instance data in the shader. if (useInstancedPointSpriteEmulation) { - structHLSL += " float3 spriteVertexPos : SPRITEPOSITION0;\n"; - structHLSL += " float2 spriteTexCoord : SPRITETEXCOORD0;\n"; + structStream << " float3 spriteVertexPos : SPRITEPOSITION0;\n" + << " float2 spriteTexCoord : SPRITETEXCOORD0;\n"; } for (size_t attributeIndex = 0; attributeIndex < shaderAttributes.size(); ++attributeIndex) @@ -434,7 +224,8 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s if (IsMatrixType(shaderAttribute.type)) { // Matrix types are always transposed - structHLSL += " " + HLSLMatrixTypeString(TransposeMatrixType(shaderAttribute.type)); + structStream << " " + << HLSLMatrixTypeString(TransposeMatrixType(shaderAttribute.type)); } else { @@ -442,31 +233,34 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s if (shaderAttribute.name == "gl_InstanceID") { - // The input type of the instance ID in HLSL (uint) differs from the one in ESSL (int). - structHLSL += " uint"; + // The input type of the instance ID in HLSL (uint) differs from the one in ESSL + // (int). + structStream << " uint"; } else { - structHLSL += " " + HLSLComponentTypeString(componentType, VariableComponentCount(shaderAttribute.type)); + structStream << " " << HLSLComponentTypeString( + componentType, + VariableComponentCount(shaderAttribute.type)); } } - structHLSL += " " + decorateVariable(shaderAttribute.name) + " : "; + structStream << " " << decorateVariable(shaderAttribute.name) << " : "; if (shaderAttribute.name == "gl_InstanceID") { - structHLSL += "SV_InstanceID"; + structStream << "SV_InstanceID"; } else { - structHLSL += "TEXCOORD" + Str(semanticIndex); + structStream << "TEXCOORD" << semanticIndex; semanticIndex += VariableRegisterCount(shaderAttribute.type); } - structHLSL += ";\n"; + structStream << ";\n"; // HLSL code for initialization - initHLSL += " " + decorateVariable(shaderAttribute.name) + " = "; + initStream << " " << decorateVariable(shaderAttribute.name) << " = "; // Mismatched vertex attribute to vertex input may result in an undefined // data reinterpretation (eg for pure integer->float, float->pure integer) @@ -474,46 +268,48 @@ std::string DynamicHLSL::generateVertexShaderForInputLayout(const std::string &s if (IsMatrixType(shaderAttribute.type) || (mRenderer->getVertexConversionType(vertexFormatType) & VERTEX_CONVERT_GPU) != 0) { - initHLSL += generateAttributeConversionHLSL(vertexFormatType, shaderAttribute); + initStream << generateAttributeConversionHLSL(vertexFormatType, shaderAttribute); } else { - initHLSL += "input." + decorateVariable(shaderAttribute.name); + initStream << "input." << decorateVariable(shaderAttribute.name); } - initHLSL += ";\n"; + initStream << ";\n"; inputIndex += VariableRowCount(TransposeMatrixType(shaderAttribute.type)); } } - std::string replacementHLSL = "struct VS_INPUT\n" - "{\n" + - structHLSL + - "};\n" - "\n" - "void initAttributes(VS_INPUT input)\n" - "{\n" + - initHLSL + - "}\n"; + structStream << "};\n" + "\n" + "void initAttributes(VS_INPUT input)\n" + "{\n" + << initStream.str() << "}\n"; std::string vertexHLSL(sourceShader); size_t copyInsertionPos = vertexHLSL.find(VERTEX_ATTRIBUTE_STUB_STRING); - vertexHLSL.replace(copyInsertionPos, VERTEX_ATTRIBUTE_STUB_STRING.length(), replacementHLSL); + vertexHLSL.replace(copyInsertionPos, VERTEX_ATTRIBUTE_STUB_STRING.length(), structStream.str()); return vertexHLSL; } -std::string DynamicHLSL::generatePixelShaderForOutputSignature(const std::string &sourceShader, const std::vector &outputVariables, - bool usesFragDepth, const std::vector &outputLayout) const +std::string DynamicHLSL::generatePixelShaderForOutputSignature( + const std::string &sourceShader, + const std::vector &outputVariables, + bool usesFragDepth, + const std::vector &outputLayout) const { - const int shaderModel = mRenderer->getMajorShaderModel(); + const int shaderModel = mRenderer->getMajorShaderModel(); std::string targetSemantic = (shaderModel >= 4) ? "SV_TARGET" : "COLOR"; - std::string depthSemantic = (shaderModel >= 4) ? "SV_Depth" : "DEPTH"; + std::string depthSemantic = (shaderModel >= 4) ? "SV_Depth" : "DEPTH"; - std::string declarationHLSL; - std::string copyHLSL; + std::stringstream declarationStream; + std::stringstream copyStream; + + declarationStream << "struct PS_OUTPUT\n" + "{\n"; for (size_t layoutIndex = 0; layoutIndex < outputLayout.size(); ++layoutIndex) { @@ -523,702 +319,576 @@ std::string DynamicHLSL::generatePixelShaderForOutputSignature(const std::string { unsigned int location = (binding - GL_COLOR_ATTACHMENT0); - const PixelShaderOutputVariable *outputVariable = FindOutputAtLocation(outputVariables, location); + const PixelShaderOutputVariable *outputVariable = + FindOutputAtLocation(outputVariables, location); // OpenGL ES 3.0 spec $4.2.1 - // If [...] not all user-defined output variables are written, the values of fragment colors + // If [...] not all user-defined output variables are written, the values of fragment + // colors // corresponding to unwritten variables are similarly undefined. if (outputVariable) { - declarationHLSL += " " + HLSLTypeString(outputVariable->type) + " " + - outputVariable->name + " : " + targetSemantic + - Str(static_cast(layoutIndex)) + ";\n"; + declarationStream << " " + HLSLTypeString(outputVariable->type) << " " + << outputVariable->name << " : " << targetSemantic + << static_cast(layoutIndex) << ";\n"; - copyHLSL += " output." + outputVariable->name + " = " + outputVariable->source + ";\n"; + copyStream << " output." << outputVariable->name << " = " + << outputVariable->source << ";\n"; } } } if (usesFragDepth) { - declarationHLSL += " float gl_Depth : " + depthSemantic + ";\n"; - copyHLSL += " output.gl_Depth = gl_Depth; \n"; + declarationStream << " float gl_Depth : " << depthSemantic << ";\n"; + copyStream << " output.gl_Depth = gl_Depth; \n"; } - std::string replacementHLSL = "struct PS_OUTPUT\n" - "{\n" + - declarationHLSL + - "};\n" - "\n" - "PS_OUTPUT generateOutput()\n" - "{\n" - " PS_OUTPUT output;\n" + - copyHLSL + - " return output;\n" - "}\n"; + declarationStream << "};\n" + "\n" + "PS_OUTPUT generateOutput()\n" + "{\n" + " PS_OUTPUT output;\n" + << copyStream.str() << " return output;\n" + "}\n"; std::string pixelHLSL(sourceShader); size_t outputInsertionPos = pixelHLSL.find(PIXEL_OUTPUT_STUB_STRING); - pixelHLSL.replace(outputInsertionPos, PIXEL_OUTPUT_STUB_STRING.length(), replacementHLSL); + pixelHLSL.replace(outputInsertionPos, PIXEL_OUTPUT_STUB_STRING.length(), + declarationStream.str()); return pixelHLSL; } -std::string DynamicHLSL::getVaryingSemantic(bool pointSize) const +void DynamicHLSL::generateVaryingLinkHLSL(ShaderType shaderType, + const VaryingPacking &varyingPacking, + std::stringstream &linkStream) const { - // SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord) - // In D3D11 we manually compute gl_PointCoord in the GS. - int shaderModel = mRenderer->getMajorShaderModel(); - return ((pointSize && shaderModel < 4) ? "COLOR" : "TEXCOORD"); -} + const auto &builtins = varyingPacking.builtins(shaderType); + ASSERT(builtins.dxPosition.enabled); + linkStream << "{\n" + << " float4 dx_Position : " << builtins.dxPosition.str() << ";\n"; -struct DynamicHLSL::SemanticInfo -{ - struct BuiltinInfo + if (builtins.glPosition.enabled) { - BuiltinInfo() - : enabled(false), - index(0), - systemValue(false) - {} - - bool enabled; - std::string semantic; - unsigned int index; - bool systemValue; - - std::string str() const - { - return (systemValue ? semantic : (semantic + Str(index))); - } - - void enableSystem(const std::string &systemValueSemantic) - { - enabled = true; - semantic = systemValueSemantic; - systemValue = true; - } - - void enable(const std::string &semanticVal, unsigned int indexVal) - { - enabled = true; - semantic = semanticVal; - index = indexVal; - } - }; - - BuiltinInfo dxPosition; - BuiltinInfo glPosition; - BuiltinInfo glFragCoord; - BuiltinInfo glPointCoord; - BuiltinInfo glPointSize; -}; - -DynamicHLSL::SemanticInfo DynamicHLSL::getSemanticInfo(int startRegisters, bool position, bool fragCoord, - bool pointCoord, bool pointSize, bool pixelShader) const -{ - SemanticInfo info; - bool hlsl4 = (mRenderer->getMajorShaderModel() >= 4); - const std::string &varyingSemantic = getVaryingSemantic(pointSize); - - int reservedRegisterIndex = startRegisters; - - if (hlsl4) - { - info.dxPosition.enableSystem("SV_Position"); - } - else if (pixelShader) - { - info.dxPosition.enableSystem("VPOS"); - } - else - { - info.dxPosition.enableSystem("POSITION"); + linkStream << " float4 gl_Position : " << builtins.glPosition.str() << ";\n"; } - if (position) + if (builtins.glFragCoord.enabled) { - info.glPosition.enable(varyingSemantic, reservedRegisterIndex++); + linkStream << " float4 gl_FragCoord : " << builtins.glFragCoord.str() << ";\n"; } - if (fragCoord) + if (builtins.glPointCoord.enabled) { - info.glFragCoord.enable(varyingSemantic, reservedRegisterIndex++); + linkStream << " float2 gl_PointCoord : " << builtins.glPointCoord.str() << ";\n"; } - if (pointCoord) + if (builtins.glPointSize.enabled) { - // SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord) - // In D3D11 we manually compute gl_PointCoord in the GS. - if (hlsl4) - { - info.glPointCoord.enable(varyingSemantic, reservedRegisterIndex++); - } - else - { - info.glPointCoord.enable("TEXCOORD", 0); - } + linkStream << " float gl_PointSize : " << builtins.glPointSize.str() << ";\n"; } - // Special case: do not include PSIZE semantic in HLSL 3 pixel shaders - if (pointSize && (!pixelShader || hlsl4)) - { - info.glPointSize.enableSystem("PSIZE"); - } + // Do this after glPointSize, to potentially combine gl_PointCoord and gl_PointSize into the + // same register. + generateVaryingHLSL(varyingPacking, linkStream); - return info; -} - -std::string DynamicHLSL::generateVaryingLinkHLSL(const SemanticInfo &info, const std::string &varyingHLSL) const -{ - std::string linkHLSL = "{\n"; - - ASSERT(info.dxPosition.enabled); - linkHLSL += " float4 dx_Position : " + info.dxPosition.str() + ";\n"; - - if (info.glPosition.enabled) - { - linkHLSL += " float4 gl_Position : " + info.glPosition.str() + ";\n"; - } - - if (info.glFragCoord.enabled) - { - linkHLSL += " float4 gl_FragCoord : " + info.glFragCoord.str() + ";\n"; - } - - if (info.glPointCoord.enabled) - { - linkHLSL += " float2 gl_PointCoord : " + info.glPointCoord.str() + ";\n"; - } - - if (info.glPointSize.enabled) - { - linkHLSL += " float gl_PointSize : " + info.glPointSize.str() + ";\n"; - } - - // Do this after glPointSize, to potentially combine gl_PointCoord and gl_PointSize into the same register. - linkHLSL += varyingHLSL; - - linkHLSL += "};\n"; - - return linkHLSL; -} - -void DynamicHLSL::storeBuiltinLinkedVaryings(const SemanticInfo &info, - std::vector *linkedVaryings) const -{ - if (info.glPosition.enabled) - { - linkedVaryings->push_back(LinkedVarying("gl_Position", GL_FLOAT_VEC4, 1, info.glPosition.semantic, - info.glPosition.index, 1)); - } - - if (info.glFragCoord.enabled) - { - linkedVaryings->push_back(LinkedVarying("gl_FragCoord", GL_FLOAT_VEC4, 1, info.glFragCoord.semantic, - info.glFragCoord.index, 1)); - } - - if (info.glPointSize.enabled) - { - linkedVaryings->push_back(LinkedVarying("gl_PointSize", GL_FLOAT, 1, "PSIZE", 0, 1)); - } -} - -void DynamicHLSL::storeUserLinkedVaryings(const std::vector &packedVaryings, - bool shaderUsesPointSize, - std::vector *linkedVaryings) const -{ - const std::string &varyingSemantic = getVaryingSemantic(shaderUsesPointSize); - - for (const PackedVarying &packedVarying : packedVaryings) - { - if (packedVarying.registerAssigned()) - { - const sh::Varying &varying = *packedVarying.varying; - - ASSERT(!varying.isBuiltIn()); - GLenum transposedType = TransposeMatrixType(varying.type); - int variableRows = (varying.isStruct() ? 1 : VariableRowCount(transposedType)); - - linkedVaryings->push_back( - LinkedVarying(varying.name, varying.type, varying.elementCount(), varyingSemantic, - packedVarying.registerIndex, variableRows * varying.elementCount())); - } - } + linkStream << "};\n"; } bool DynamicHLSL::generateShaderLinkHLSL(const gl::Data &data, const gl::Program::Data &programData, - InfoLog &infoLog, - int registers, - std::string &pixelHLSL, - std::string &vertexHLSL, - const std::vector &packedVaryings, - std::vector *linkedVaryings, - std::vector *outPixelShaderKey, - bool *outUsesFragDepth) const + const ProgramD3DMetadata &programMetadata, + const VaryingPacking &varyingPacking, + std::string *pixelHLSL, + std::string *vertexHLSL) const { - if (pixelHLSL.empty() || vertexHLSL.empty()) - { - return false; - } + ASSERT(pixelHLSL->empty() && vertexHLSL->empty()); const gl::Shader *vertexShaderGL = programData.getAttachedVertexShader(); const ShaderD3D *vertexShader = GetImplAs(vertexShaderGL); const gl::Shader *fragmentShaderGL = programData.getAttachedFragmentShader(); const ShaderD3D *fragmentShader = GetImplAs(fragmentShaderGL); + const int shaderModel = mRenderer->getMajorShaderModel(); - bool usesMRT = fragmentShader->mUsesMultipleRenderTargets; - bool usesFragCoord = fragmentShader->mUsesFragCoord; - bool usesPointCoord = fragmentShader->mUsesPointCoord; - bool usesPointSize = vertexShader->mUsesPointSize; - bool useInstancedPointSpriteEmulation = usesPointSize && mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; + bool useInstancedPointSpriteEmulation = + programMetadata.usesPointSize() && + mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; // Validation done in the compiler - ASSERT(!fragmentShader->mUsesFragColor || !fragmentShader->mUsesFragData); + ASSERT(!fragmentShader->usesFragColor() || !fragmentShader->usesFragData()); - // Write the HLSL input/output declarations - const int shaderModel = mRenderer->getMajorShaderModel(); - const int registersNeeded = registers + (usesFragCoord ? 1 : 0) + (usesPointCoord ? 1 : 0); - - // Two cases when writing to gl_FragColor and using ESSL 1.0: - // - with a 3.0 context, the output color is copied to channel 0 - // - with a 2.0 context, the output color is broadcast to all channels - const bool broadcast = (fragmentShader->mUsesFragColor && data.clientVersion < 3); - const unsigned int numRenderTargets = (broadcast || usesMRT ? data.caps->maxDrawBuffers : 1); - - // gl_Position only needs to be outputted from the vertex shader if transform feedback is active. - // This isn't supported on D3D11 Feature Level 9_3, so we don't output gl_Position from the vertex shader in this case. - // This saves us 1 output vector. - bool outputPositionFromVS = !(shaderModel >= 4 && mRenderer->getShaderModelSuffix() != ""); - - int shaderVersion = vertexShaderGL->getShaderVersion(); - - if (static_cast(registersNeeded) > data.caps->maxVaryingVectors) - { - infoLog << "No varying registers left to support gl_FragCoord/gl_PointCoord"; - return false; - } - - const std::string &varyingHLSL = generateVaryingHLSL(packedVaryings, usesPointSize); - - // Instanced PointSprite emulation requires that gl_PointCoord is present in the vertex shader VS_OUTPUT - // structure to ensure compatibility with the generated PS_INPUT of the pixel shader. - // GeometryShader PointSprite emulation does not require this additional entry because the - // GS_OUTPUT of the Geometry shader contains the pointCoord value and already matches the PS_INPUT of the - // generated pixel shader. - // The Geometry Shader point sprite implementation needs gl_PointSize to be in VS_OUTPUT and GS_INPUT. - // Instanced point sprites doesn't need gl_PointSize in VS_OUTPUT. - const SemanticInfo &vertexSemantics = getSemanticInfo(registers, outputPositionFromVS, - usesFragCoord, (useInstancedPointSpriteEmulation && usesPointCoord), - (!useInstancedPointSpriteEmulation && usesPointSize), false); - - storeUserLinkedVaryings(packedVaryings, usesPointSize, linkedVaryings); - storeBuiltinLinkedVaryings(vertexSemantics, linkedVaryings); + std::stringstream vertexStream; + vertexStream << vertexShaderGL->getTranslatedSource(); // Instanced PointSprite emulation requires additional entries originally generated in the - // GeometryShader HLSL. These include pointsize clamp values. + // GeometryShader HLSL. These include pointsize clamp values. if (useInstancedPointSpriteEmulation) { - vertexHLSL += "static float minPointSize = " + Str((int)mRenderer->getRendererCaps().minAliasedPointSize) + ".0f;\n" - "static float maxPointSize = " + Str((int)mRenderer->getRendererCaps().maxAliasedPointSize) + ".0f;\n"; + vertexStream << "static float minPointSize = " + << static_cast(data.caps->minAliasedPointSize) << ".0f;\n" + << "static float maxPointSize = " + << static_cast(data.caps->maxAliasedPointSize) << ".0f;\n"; } // Add stub string to be replaced when shader is dynamically defined by its layout - vertexHLSL += "\n" + VERTEX_ATTRIBUTE_STUB_STRING + "\n" - "struct VS_OUTPUT\n" + generateVaryingLinkHLSL(vertexSemantics, varyingHLSL) + "\n" - "VS_OUTPUT main(VS_INPUT input)\n" - "{\n" - " initAttributes(input);\n"; + vertexStream << "\n" << VERTEX_ATTRIBUTE_STUB_STRING + "\n"; + + // Write the HLSL input/output declarations + vertexStream << "struct VS_OUTPUT\n"; + generateVaryingLinkHLSL(SHADER_VERTEX, varyingPacking, vertexStream); + vertexStream << "\n" + << "VS_OUTPUT main(VS_INPUT input)\n" + << "{\n" + << " initAttributes(input);\n"; if (vertexShader->usesDeferredInit()) { - vertexHLSL += "\n" - " initializeDeferredGlobals();\n"; + vertexStream << "\n" + << " initializeDeferredGlobals();\n"; } - vertexHLSL += "\n" - " gl_main();\n" - "\n" - " VS_OUTPUT output;\n"; + vertexStream << "\n" + << " gl_main();\n" + << "\n" + << " VS_OUTPUT output;\n"; - if (outputPositionFromVS) + const auto &vertexBuiltins = varyingPacking.builtins(SHADER_VERTEX); + + if (vertexBuiltins.glPosition.enabled) { - vertexHLSL += " output.gl_Position = gl_Position;\n"; + vertexStream << " output.gl_Position = gl_Position;\n"; } // On D3D9 or D3D11 Feature Level 9, we need to emulate large viewports using dx_ViewAdjust. - if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "") + if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "") { - vertexHLSL += " output.dx_Position.x = gl_Position.x;\n" - " output.dx_Position.y = -gl_Position.y;\n" - " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" - " output.dx_Position.w = gl_Position.w;\n"; + vertexStream << " output.dx_Position.x = gl_Position.x;\n" + << " output.dx_Position.y = -gl_Position.y;\n" + << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" + << " output.dx_Position.w = gl_Position.w;\n"; } else { - vertexHLSL += " output.dx_Position.x = gl_Position.x * dx_ViewAdjust.z + dx_ViewAdjust.x * gl_Position.w;\n" - " output.dx_Position.y = -(gl_Position.y * dx_ViewAdjust.w + dx_ViewAdjust.y * gl_Position.w);\n" - " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" - " output.dx_Position.w = gl_Position.w;\n"; + vertexStream << " output.dx_Position.x = gl_Position.x * dx_ViewAdjust.z + " + "dx_ViewAdjust.x * gl_Position.w;\n" + << " output.dx_Position.y = -(gl_Position.y * dx_ViewAdjust.w + " + "dx_ViewAdjust.y * gl_Position.w);\n" + << " output.dx_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n" + << " output.dx_Position.w = gl_Position.w;\n"; } // We don't need to output gl_PointSize if we use are emulating point sprites via instancing. - if (usesPointSize && shaderModel >= 3 && !useInstancedPointSpriteEmulation) + if (vertexBuiltins.glPointSize.enabled) { - vertexHLSL += " output.gl_PointSize = gl_PointSize;\n"; + vertexStream << " output.gl_PointSize = gl_PointSize;\n"; } - if (usesFragCoord) + if (vertexBuiltins.glFragCoord.enabled) { - vertexHLSL += " output.gl_FragCoord = gl_Position;\n"; + vertexStream << " output.gl_FragCoord = gl_Position;\n"; } - for (const PackedVarying &packedVarying : packedVaryings) + for (const PackedVaryingRegister ®isterInfo : varyingPacking.getRegisterList()) { - if (!packedVarying.registerAssigned()) + const auto &packedVarying = *registerInfo.packedVarying; + const auto &varying = *packedVarying.varying; + ASSERT(!varying.isStruct()); + + vertexStream << " output.v" << registerInfo.semanticIndex << " = "; + + if (packedVarying.isStructField()) { - continue; + vertexStream << decorateVariable(packedVarying.parentStructName) << "."; } - const sh::Varying &varying = *packedVarying.varying; + vertexStream << decorateVariable(varying.name); - for (unsigned int elementIndex = 0; elementIndex < varying.elementCount(); elementIndex++) + if (varying.isArray()) { - int variableRows = - (varying.isStruct() ? 1 : VariableRowCount(TransposeMatrixType(varying.type))); - - for (int row = 0; row < variableRows; row++) - { - int r = packedVarying.registerIndex + - packedVarying.columnIndex * data.caps->maxVaryingVectors + - elementIndex * variableRows + row; - vertexHLSL += " output.v" + Str(r); - - vertexHLSL += " = _" + varying.name; - - if (varying.isArray()) - { - vertexHLSL += ArrayString(elementIndex); - } - - if (variableRows > 1) - { - vertexHLSL += ArrayString(row); - } - - vertexHLSL += ";\n"; - } + WriteArrayString(vertexStream, registerInfo.varyingArrayIndex); } + + if (VariableRowCount(varying.type) > 1) + { + WriteArrayString(vertexStream, registerInfo.varyingRowIndex); + } + + vertexStream << ";\n"; } // Instanced PointSprite emulation requires additional entries to calculate // the final output vertex positions of the quad that represents each sprite. if (useInstancedPointSpriteEmulation) { - vertexHLSL += "\n" - " gl_PointSize = clamp(gl_PointSize, minPointSize, maxPointSize);\n" - " output.dx_Position.xyz += float3(input.spriteVertexPos.x * gl_PointSize / (dx_ViewCoords.x*2), input.spriteVertexPos.y * gl_PointSize / (dx_ViewCoords.y*2), input.spriteVertexPos.z) * output.dx_Position.w;\n"; + vertexStream << "\n" + << " gl_PointSize = clamp(gl_PointSize, minPointSize, maxPointSize);\n" + << " output.dx_Position.xyz += float3(input.spriteVertexPos.x * " + "gl_PointSize / (dx_ViewCoords.x*2), input.spriteVertexPos.y * " + "gl_PointSize / (dx_ViewCoords.y*2), input.spriteVertexPos.z) * " + "output.dx_Position.w;\n"; - if (usesPointCoord) + if (programMetadata.usesPointCoord()) { - vertexHLSL += "\n" - " output.gl_PointCoord = input.spriteTexCoord;\n"; + vertexStream << "\n" + << " output.gl_PointCoord = input.spriteTexCoord;\n"; } } - vertexHLSL += "\n" - " return output;\n" - "}\n"; - - const SemanticInfo &pixelSemantics = getSemanticInfo(registers, outputPositionFromVS, usesFragCoord, usesPointCoord, - (!useInstancedPointSpriteEmulation && usesPointSize), true); - - pixelHLSL += "struct PS_INPUT\n" + generateVaryingLinkHLSL(pixelSemantics, varyingHLSL) + "\n"; - - if (shaderVersion < 300) + // Renderers that enable instanced pointsprite emulation require the vertex shader output member + // gl_PointCoord to be set to a default value if used without gl_PointSize. 0.5,0.5 is the same + // default value used in the generated pixel shader. + if (programMetadata.usesInsertedPointCoordValue()) { - for (unsigned int renderTargetIndex = 0; renderTargetIndex < numRenderTargets; renderTargetIndex++) - { - PixelShaderOutputVariable outputKeyVariable; - outputKeyVariable.type = GL_FLOAT_VEC4; - outputKeyVariable.name = "gl_Color" + Str(renderTargetIndex); - outputKeyVariable.source = broadcast ? "gl_Color[0]" : "gl_Color[" + Str(renderTargetIndex) + "]"; - outputKeyVariable.outputIndex = renderTargetIndex; - - outPixelShaderKey->push_back(outputKeyVariable); - } - - *outUsesFragDepth = fragmentShader->mUsesFragDepth; - } - else - { - const auto &shaderOutputVars = fragmentShaderGL->getActiveOutputVariables(); - - for (auto outputPair : programData.getOutputVariables()) - { - const VariableLocation &outputLocation = outputPair.second; - const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index]; - const std::string &variableName = "out_" + outputLocation.name; - const std::string &elementString = (outputLocation.element == GL_INVALID_INDEX ? "" : Str(outputLocation.element)); - - ASSERT(outputVariable.staticUse); - - PixelShaderOutputVariable outputKeyVariable; - outputKeyVariable.type = outputVariable.type; - outputKeyVariable.name = variableName + elementString; - outputKeyVariable.source = variableName + ArrayString(outputLocation.element); - outputKeyVariable.outputIndex = outputPair.first; - - outPixelShaderKey->push_back(outputKeyVariable); - } - - *outUsesFragDepth = false; + ASSERT(!useInstancedPointSpriteEmulation); + vertexStream << "\n" + << " output.gl_PointCoord = float2(0.5, 0.5);\n"; } - pixelHLSL += PIXEL_OUTPUT_STUB_STRING + "\n"; + vertexStream << "\n" + << " return output;\n" + << "}\n"; - if (fragmentShader->mUsesFrontFacing) + std::stringstream pixelStream; + pixelStream << fragmentShaderGL->getTranslatedSource(); + pixelStream << "struct PS_INPUT\n"; + generateVaryingLinkHLSL(SHADER_PIXEL, varyingPacking, pixelStream); + pixelStream << "\n"; + + pixelStream << PIXEL_OUTPUT_STUB_STRING + "\n"; + + if (fragmentShader->usesFrontFacing()) { if (shaderModel >= 4) { - pixelHLSL += "PS_OUTPUT main(PS_INPUT input, bool isFrontFace : SV_IsFrontFace)\n" - "{\n"; + pixelStream << "PS_OUTPUT main(PS_INPUT input, bool isFrontFace : SV_IsFrontFace)\n" + << "{\n"; } else { - pixelHLSL += "PS_OUTPUT main(PS_INPUT input, float vFace : VFACE)\n" - "{\n"; + pixelStream << "PS_OUTPUT main(PS_INPUT input, float vFace : VFACE)\n" + << "{\n"; } } else { - pixelHLSL += "PS_OUTPUT main(PS_INPUT input)\n" - "{\n"; + pixelStream << "PS_OUTPUT main(PS_INPUT input)\n" + << "{\n"; } - if (usesFragCoord) + const auto &pixelBuiltins = varyingPacking.builtins(SHADER_PIXEL); + + if (pixelBuiltins.glFragCoord.enabled) { - pixelHLSL += " float rhw = 1.0 / input.gl_FragCoord.w;\n"; + pixelStream << " float rhw = 1.0 / input.gl_FragCoord.w;\n"; // Certain Shader Models (4_0+ and 3_0) allow reading from dx_Position in the pixel shader. - // Other Shader Models (4_0_level_9_3 and 2_x) don't support this, so we emulate it using dx_ViewCoords. + // Other Shader Models (4_0_level_9_3 and 2_x) don't support this, so we emulate it using + // dx_ViewCoords. if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "") { - pixelHLSL += " gl_FragCoord.x = input.dx_Position.x;\n" - " gl_FragCoord.y = input.dx_Position.y;\n"; + pixelStream << " gl_FragCoord.x = input.dx_Position.x;\n" + << " gl_FragCoord.y = input.dx_Position.y;\n"; } else if (shaderModel == 3) { - pixelHLSL += " gl_FragCoord.x = input.dx_Position.x + 0.5;\n" - " gl_FragCoord.y = input.dx_Position.y + 0.5;\n"; + pixelStream << " gl_FragCoord.x = input.dx_Position.x + 0.5;\n" + << " gl_FragCoord.y = input.dx_Position.y + 0.5;\n"; } else { - // dx_ViewCoords contains the viewport width/2, height/2, center.x and center.y. See Renderer::setViewport() - pixelHLSL += " gl_FragCoord.x = (input.gl_FragCoord.x * rhw) * dx_ViewCoords.x + dx_ViewCoords.z;\n" - " gl_FragCoord.y = (input.gl_FragCoord.y * rhw) * dx_ViewCoords.y + dx_ViewCoords.w;\n"; + // dx_ViewCoords contains the viewport width/2, height/2, center.x and center.y. See + // Renderer::setViewport() + pixelStream << " gl_FragCoord.x = (input.gl_FragCoord.x * rhw) * dx_ViewCoords.x + " + "dx_ViewCoords.z;\n" + << " gl_FragCoord.y = (input.gl_FragCoord.y * rhw) * dx_ViewCoords.y + " + "dx_ViewCoords.w;\n"; } - pixelHLSL += " gl_FragCoord.z = (input.gl_FragCoord.z * rhw) * dx_DepthFront.x + dx_DepthFront.y;\n" - " gl_FragCoord.w = rhw;\n"; + pixelStream << " gl_FragCoord.z = (input.gl_FragCoord.z * rhw) * dx_DepthFront.x + " + "dx_DepthFront.y;\n" + << " gl_FragCoord.w = rhw;\n"; } - if (usesPointCoord && shaderModel >= 3) + if (pixelBuiltins.glPointCoord.enabled && shaderModel >= 3) { - pixelHLSL += " gl_PointCoord.x = input.gl_PointCoord.x;\n"; - pixelHLSL += " gl_PointCoord.y = 1.0 - input.gl_PointCoord.y;\n"; + pixelStream << " gl_PointCoord.x = input.gl_PointCoord.x;\n" + << " gl_PointCoord.y = 1.0 - input.gl_PointCoord.y;\n"; } - if (fragmentShader->mUsesFrontFacing) + if (fragmentShader->usesFrontFacing()) { if (shaderModel <= 3) { - pixelHLSL += " gl_FrontFacing = (vFace * dx_DepthFront.z >= 0.0);\n"; + pixelStream << " gl_FrontFacing = (vFace * dx_DepthFront.z >= 0.0);\n"; } else { - pixelHLSL += " gl_FrontFacing = isFrontFace;\n"; + pixelStream << " gl_FrontFacing = isFrontFace;\n"; } } - for (const PackedVarying &packedVarying : packedVaryings) + for (const PackedVaryingRegister ®isterInfo : varyingPacking.getRegisterList()) { - const sh::Varying &varying = *packedVarying.varying; - - if (!packedVarying.registerAssigned()) - { - ASSERT(varying.isBuiltIn() || !varying.staticUse); - continue; - } + const auto &packedVarying = *registerInfo.packedVarying; + const auto &varying = *packedVarying.varying; + ASSERT(!varying.isBuiltIn() && !varying.isStruct()); // Don't reference VS-only transform feedback varyings in the PS. - if (packedVarying.vertexOnly) + if (registerInfo.packedVarying->vertexOnly) continue; - ASSERT(!varying.isBuiltIn()); - for (unsigned int elementIndex = 0; elementIndex < varying.elementCount(); elementIndex++) + pixelStream << " "; + + if (packedVarying.isStructField()) { - GLenum transposedType = TransposeMatrixType(varying.type); - int variableRows = (varying.isStruct() ? 1 : VariableRowCount(transposedType)); - for (int row = 0; row < variableRows; row++) - { - std::string n = Str(packedVarying.registerIndex + - packedVarying.columnIndex * data.caps->maxVaryingVectors + - elementIndex * variableRows + row); - pixelHLSL += " _" + varying.name; - - if (varying.isArray()) - { - pixelHLSL += ArrayString(elementIndex); - } - - if (variableRows > 1) - { - pixelHLSL += ArrayString(row); - } - - if (varying.isStruct()) - { - pixelHLSL += " = input.v" + n + ";\n"; - break; - } - else - { - switch (VariableColumnCount(transposedType)) - { - case 1: - pixelHLSL += " = input.v" + n + ".x;\n"; - break; - case 2: - pixelHLSL += " = input.v" + n + ".xy;\n"; - break; - case 3: - pixelHLSL += " = input.v" + n + ".xyz;\n"; - break; - case 4: - pixelHLSL += " = input.v" + n + ";\n"; - break; - default: - UNREACHABLE(); - } - } - } + pixelStream << decorateVariable(packedVarying.parentStructName) << "."; } + + pixelStream << decorateVariable(varying.name); + + if (varying.isArray()) + { + WriteArrayString(pixelStream, registerInfo.varyingArrayIndex); + } + + GLenum transposedType = TransposeMatrixType(varying.type); + if (VariableRowCount(transposedType) > 1) + { + WriteArrayString(pixelStream, registerInfo.varyingRowIndex); + } + + pixelStream << " = input.v" << registerInfo.semanticIndex; + + switch (VariableColumnCount(transposedType)) + { + case 1: + pixelStream << ".x"; + break; + case 2: + pixelStream << ".xy"; + break; + case 3: + pixelStream << ".xyz"; + break; + case 4: + break; + default: + UNREACHABLE(); + } + pixelStream << ";\n"; } if (fragmentShader->usesDeferredInit()) { - pixelHLSL += "\n" - " initializeDeferredGlobals();\n"; + pixelStream << "\n" + << " initializeDeferredGlobals();\n"; } - pixelHLSL += "\n" - " gl_main();\n" - "\n" - " return generateOutput();\n" - "}\n"; + pixelStream << "\n" + << " gl_main();\n" + << "\n" + << " return generateOutput();\n" + << "}\n"; + + *vertexHLSL = vertexStream.str(); + *pixelHLSL = pixelStream.str(); return true; } -std::string DynamicHLSL::generateGeometryShaderHLSL( - int registers, - const ShaderD3D *fragmentShader, - const std::vector &packedVaryings) const +std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &varyingPacking) const { - // for now we only handle point sprite emulation ASSERT(mRenderer->getMajorShaderModel() >= 4); - return generatePointSpriteHLSL(registers, fragmentShader, packedVaryings); + + std::stringstream preambleStream; + + const auto &builtins = varyingPacking.builtins(SHADER_VERTEX); + + preambleStream << "struct GS_INPUT\n"; + generateVaryingLinkHLSL(SHADER_VERTEX, varyingPacking, preambleStream); + preambleStream << "\n" + << "struct GS_OUTPUT\n"; + generateVaryingLinkHLSL(SHADER_GEOMETRY, varyingPacking, preambleStream); + preambleStream + << "\n" + << "void copyVertex(inout GS_OUTPUT output, GS_INPUT input, GS_INPUT flatinput)\n" + << "{\n" + << " output.gl_Position = input.gl_Position;\n"; + + if (builtins.glPointSize.enabled) + { + preambleStream << " output.gl_PointSize = input.gl_PointSize;\n"; + } + + for (const PackedVaryingRegister &varyingRegister : varyingPacking.getRegisterList()) + { + preambleStream << " output.v" << varyingRegister.semanticIndex << " = "; + if (varyingRegister.packedVarying->interpolation == sh::INTERPOLATION_FLAT) + { + preambleStream << "flat"; + } + preambleStream << "input.v" << varyingRegister.semanticIndex << "; \n"; + } + + if (builtins.glFragCoord.enabled) + { + preambleStream << " output.gl_FragCoord = input.gl_FragCoord;\n"; + } + + // Only write the dx_Position if we aren't using point sprites + preambleStream << "#ifndef ANGLE_POINT_SPRITE_SHADER\n" + << " output.dx_Position = input.dx_Position;\n" + << "#endif // ANGLE_POINT_SPRITE_SHADER\n" + << "}\n"; + + return preambleStream.str(); } -std::string DynamicHLSL::generatePointSpriteHLSL( - int registers, - const ShaderD3D *fragmentShader, - const std::vector &packedVaryings) const +std::string DynamicHLSL::generateGeometryShaderHLSL(gl::PrimitiveType primitiveType, + const gl::Data &data, + const gl::Program::Data &programData, + const std::string &preambleString) const { - ASSERT(registers >= 0); ASSERT(mRenderer->getMajorShaderModel() >= 4); - std::string geomHLSL; + std::stringstream shaderStream; - const SemanticInfo &inSemantics = getSemanticInfo(registers, true, fragmentShader->mUsesFragCoord, - false, true, false); - const SemanticInfo &outSemantics = getSemanticInfo(registers, true, fragmentShader->mUsesFragCoord, - fragmentShader->mUsesPointCoord, true, false); + const bool pointSprites = (primitiveType == PRIMITIVE_POINTS); + const bool usesPointCoord = preambleString.find("gl_PointCoord") != std::string::npos; - // If we're generating the geometry shader, we assume the vertex shader uses point size. - std::string varyingHLSL = generateVaryingHLSL(packedVaryings, true); - std::string inLinkHLSL = generateVaryingLinkHLSL(inSemantics, varyingHLSL); - std::string outLinkHLSL = generateVaryingLinkHLSL(outSemantics, varyingHLSL); + const char *inputPT = nullptr; + const char *outputPT = nullptr; + int inputSize = 0; + int maxVertexOutput = 0; - // TODO(geofflang): use context's caps - geomHLSL += "uniform float4 dx_ViewCoords : register(c1);\n" - "\n" - "struct GS_INPUT\n" + inLinkHLSL + "\n" + - "struct GS_OUTPUT\n" + outLinkHLSL + "\n" + - "\n" - "static float2 pointSpriteCorners[] = \n" - "{\n" - " float2( 0.5f, -0.5f),\n" - " float2( 0.5f, 0.5f),\n" - " float2(-0.5f, -0.5f),\n" - " float2(-0.5f, 0.5f)\n" - "};\n" - "\n" - "static float2 pointSpriteTexcoords[] = \n" - "{\n" - " float2(1.0f, 1.0f),\n" - " float2(1.0f, 0.0f),\n" - " float2(0.0f, 1.0f),\n" - " float2(0.0f, 0.0f)\n" - "};\n" - "\n" - "static float minPointSize = " + Str(static_cast(mRenderer->getRendererCaps().minAliasedPointSize)) + ".0f;\n" - "static float maxPointSize = " + Str(static_cast(mRenderer->getRendererCaps().maxAliasedPointSize)) + ".0f;\n" - "\n" - "[maxvertexcount(4)]\n" - "void main(point GS_INPUT input[1], inout TriangleStream outStream)\n" - "{\n" - " GS_OUTPUT output = (GS_OUTPUT)0;\n" - " output.gl_Position = input[0].gl_Position;\n" - " output.gl_PointSize = input[0].gl_PointSize;\n"; - - for (int r = 0; r < registers; r++) + switch (primitiveType) { - geomHLSL += " output.v" + Str(r) + " = input[0].v" + Str(r) + ";\n"; + case PRIMITIVE_POINTS: + inputPT = "point"; + outputPT = "Triangle"; + inputSize = 1; + maxVertexOutput = 4; + break; + + case PRIMITIVE_LINES: + case PRIMITIVE_LINE_STRIP: + case PRIMITIVE_LINE_LOOP: + inputPT = "line"; + outputPT = "Line"; + inputSize = 2; + maxVertexOutput = 2; + break; + + case PRIMITIVE_TRIANGLES: + case PRIMITIVE_TRIANGLE_STRIP: + case PRIMITIVE_TRIANGLE_FAN: + inputPT = "triangle"; + outputPT = "Triangle"; + inputSize = 3; + maxVertexOutput = 3; + break; + + default: + UNREACHABLE(); + break; } - if (fragmentShader->mUsesFragCoord) + if (pointSprites) { - geomHLSL += " output.gl_FragCoord = input[0].gl_FragCoord;\n"; + shaderStream << "#define ANGLE_POINT_SPRITE_SHADER\n" + "\n" + "uniform float4 dx_ViewCoords : register(c1);\n" + "\n" + "static float2 pointSpriteCorners[] = \n" + "{\n" + " float2( 0.5f, -0.5f),\n" + " float2( 0.5f, 0.5f),\n" + " float2(-0.5f, -0.5f),\n" + " float2(-0.5f, 0.5f)\n" + "};\n" + "\n" + "static float2 pointSpriteTexcoords[] = \n" + "{\n" + " float2(1.0f, 1.0f),\n" + " float2(1.0f, 0.0f),\n" + " float2(0.0f, 1.0f),\n" + " float2(0.0f, 0.0f)\n" + "};\n" + "\n" + "static float minPointSize = " + << static_cast(data.caps->minAliasedPointSize) + << ".0f;\n" + "static float maxPointSize = " + << static_cast(data.caps->maxAliasedPointSize) << ".0f;\n" + << "\n"; } - geomHLSL += " \n" - " float gl_PointSize = clamp(input[0].gl_PointSize, minPointSize, maxPointSize);\n" - " float4 dx_Position = input[0].dx_Position;\n" - " float2 viewportScale = float2(1.0f / dx_ViewCoords.x, 1.0f / dx_ViewCoords.y) * dx_Position.w;\n"; + shaderStream << preambleString << "\n" + << "[maxvertexcount(" << maxVertexOutput << ")]\n" + << "void main(" << inputPT << " GS_INPUT input[" << inputSize << "], "; - for (int corner = 0; corner < 4; corner++) + if (primitiveType == PRIMITIVE_TRIANGLE_STRIP) { - geomHLSL += " \n" - " output.dx_Position = dx_Position + float4(pointSpriteCorners[" + Str(corner) + "] * viewportScale * gl_PointSize, 0.0f, 0.0f);\n"; + shaderStream << "uint primitiveID : SV_PrimitiveID, "; + } - if (fragmentShader->mUsesPointCoord) + shaderStream << " inout " << outputPT << "Stream outStream)\n" + << "{\n" + << " GS_OUTPUT output = (GS_OUTPUT)0;\n"; + + if (primitiveType == PRIMITIVE_TRIANGLE_STRIP) + { + shaderStream << " uint lastVertexIndex = (primitiveID % 2 == 0 ? 2 : 1);\n"; + } + else + { + shaderStream << " uint lastVertexIndex = " << (inputSize - 1) << ";\n"; + } + + for (int vertexIndex = 0; vertexIndex < inputSize; ++vertexIndex) + { + shaderStream << " copyVertex(output, input[" << vertexIndex + << "], input[lastVertexIndex]);\n"; + + if (!pointSprites) { - geomHLSL += " output.gl_PointCoord = pointSpriteTexcoords[" + Str(corner) + "];\n"; + ASSERT(inputSize == maxVertexOutput); + shaderStream << " outStream.Append(output);\n"; } - - geomHLSL += " outStream.Append(output);\n"; } - geomHLSL += " \n" - " outStream.RestartStrip();\n" - "}\n"; + if (pointSprites) + { + shaderStream << "\n" + " float4 dx_Position = input[0].dx_Position;\n" + " float gl_PointSize = clamp(input[0].gl_PointSize, minPointSize, " + "maxPointSize);\n" + " float2 viewportScale = float2(1.0f / dx_ViewCoords.x, 1.0f / " + "dx_ViewCoords.y) * dx_Position.w;\n"; - return geomHLSL; + for (int corner = 0; corner < 4; corner++) + { + shaderStream << "\n" + " output.dx_Position = dx_Position + float4(pointSpriteCorners[" + << corner << "] * viewportScale * gl_PointSize, 0.0f, 0.0f);\n"; + + if (usesPointCoord) + { + shaderStream << " output.gl_PointCoord = pointSpriteTexcoords[" << corner + << "];\n"; + } + + shaderStream << " outStream.Append(output);\n"; + } + } + + shaderStream << " \n" + " outStream.RestartStrip();\n" + "}\n"; + + return shaderStream.str(); } // This method needs to match OutputHLSL::decorate @@ -1232,11 +902,12 @@ std::string DynamicHLSL::decorateVariable(const std::string &name) return name; } -std::string DynamicHLSL::generateAttributeConversionHLSL(gl::VertexFormatType vertexFormatType, - const sh::ShaderVariable &shaderAttrib) const +std::string DynamicHLSL::generateAttributeConversionHLSL( + gl::VertexFormatType vertexFormatType, + const sh::ShaderVariable &shaderAttrib) const { const gl::VertexFormat &vertexFormat = gl::GetVertexFormatFromType(vertexFormatType); - std::string attribString = "input." + decorateVariable(shaderAttrib.name); + std::string attribString = "input." + decorateVariable(shaderAttrib.name); // Matrix if (IsMatrixType(shaderAttrib.type)) @@ -1245,10 +916,11 @@ std::string DynamicHLSL::generateAttributeConversionHLSL(gl::VertexFormatType ve } GLenum shaderComponentType = VariableComponentType(shaderAttrib.type); - int shaderComponentCount = VariableComponentCount(shaderAttrib.type); + int shaderComponentCount = VariableComponentCount(shaderAttrib.type); // Perform integer to float conversion (if necessary) - bool requiresTypeConversion = (shaderComponentType == GL_FLOAT && vertexFormat.type != GL_FLOAT); + bool requiresTypeConversion = + (shaderComponentType == GL_FLOAT && vertexFormat.type != GL_FLOAT); if (requiresTypeConversion) { @@ -1261,4 +933,57 @@ std::string DynamicHLSL::generateAttributeConversionHLSL(gl::VertexFormatType ve return attribString; } +void DynamicHLSL::getPixelShaderOutputKey(const gl::Data &data, + const gl::Program::Data &programData, + const ProgramD3DMetadata &metadata, + std::vector *outPixelShaderKey) +{ + // Two cases when writing to gl_FragColor and using ESSL 1.0: + // - with a 3.0 context, the output color is copied to channel 0 + // - with a 2.0 context, the output color is broadcast to all channels + bool broadcast = metadata.usesBroadcast(data); + const unsigned int numRenderTargets = + (broadcast || metadata.usesMultipleFragmentOuts() ? data.caps->maxDrawBuffers : 1); + + if (metadata.getMajorShaderVersion() < 300) + { + for (unsigned int renderTargetIndex = 0; renderTargetIndex < numRenderTargets; + renderTargetIndex++) + { + PixelShaderOutputVariable outputKeyVariable; + outputKeyVariable.type = GL_FLOAT_VEC4; + outputKeyVariable.name = "gl_Color" + Str(renderTargetIndex); + outputKeyVariable.source = + broadcast ? "gl_Color[0]" : "gl_Color[" + Str(renderTargetIndex) + "]"; + outputKeyVariable.outputIndex = renderTargetIndex; + + outPixelShaderKey->push_back(outputKeyVariable); + } + } + else + { + const auto &shaderOutputVars = + metadata.getFragmentShader()->getData().getActiveOutputVariables(); + + for (auto outputPair : programData.getOutputVariables()) + { + const VariableLocation &outputLocation = outputPair.second; + const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index]; + const std::string &variableName = "out_" + outputLocation.name; + const std::string &elementString = + (outputLocation.element == GL_INVALID_INDEX ? "" : Str(outputLocation.element)); + + ASSERT(outputVariable.staticUse); + + PixelShaderOutputVariable outputKeyVariable; + outputKeyVariable.type = outputVariable.type; + outputKeyVariable.name = variableName + elementString; + outputKeyVariable.source = variableName + ArrayString(outputLocation.element); + outputKeyVariable.outputIndex = outputPair.first; + + outPixelShaderKey->push_back(outputKeyVariable); + } + } } + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h index 45290339207e..df52d245cfc0 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h @@ -17,6 +17,7 @@ #include "libANGLE/Constants.h" #include "libANGLE/Program.h" #include "libANGLE/formatutils.h" +#include "libANGLE/renderer/d3d/RendererD3D.h" namespace sh { @@ -28,15 +29,16 @@ namespace gl { class InfoLog; struct VariableLocation; -struct LinkedVarying; struct VertexAttribute; struct Data; } namespace rx { -class RendererD3D; +struct PackedVarying; +class ProgramD3DMetadata; class ShaderD3D; +class VaryingPacking; struct PixelShaderOutputVariable { @@ -46,75 +48,47 @@ struct PixelShaderOutputVariable size_t outputIndex; }; -struct PackedVarying -{ - PackedVarying(const sh::Varying &varyingIn) - : varying(&varyingIn), registerIndex(GL_INVALID_INDEX), columnIndex(0), vertexOnly(false) - { - } - - bool registerAssigned() const { return registerIndex != GL_INVALID_INDEX; } - - void resetRegisterAssignment() { registerIndex = GL_INVALID_INDEX; } - - const sh::Varying *varying; - - // Assigned during link - unsigned int registerIndex; - - // Assigned during link, Defaults to 0 - unsigned int columnIndex; - - // Transform feedback varyings can be only referenced in the VS. - bool vertexOnly; -}; - class DynamicHLSL : angle::NonCopyable { public: explicit DynamicHLSL(RendererD3D *const renderer); - int packVaryings(gl::InfoLog &infoLog, - std::vector *packedVaryings, - const std::vector &transformFeedbackVaryings); - std::string generateVertexShaderForInputLayout(const std::string &sourceShader, - const gl::InputLayout &inputLayout, - const std::vector &shaderAttributes) const; - std::string generatePixelShaderForOutputSignature(const std::string &sourceShader, const std::vector &outputVariables, - bool usesFragDepth, const std::vector &outputLayout) const; + std::string generateVertexShaderForInputLayout( + const std::string &sourceShader, + const gl::InputLayout &inputLayout, + const std::vector &shaderAttributes) const; + std::string generatePixelShaderForOutputSignature( + const std::string &sourceShader, + const std::vector &outputVariables, + bool usesFragDepth, + const std::vector &outputLayout) const; bool generateShaderLinkHLSL(const gl::Data &data, const gl::Program::Data &programData, - gl::InfoLog &infoLog, - int registers, - std::string &pixelHLSL, - std::string &vertexHLSL, - const std::vector &packedVaryings, - std::vector *linkedVaryings, - std::vector *outPixelShaderKey, - bool *outUsesFragDepth) const; + const ProgramD3DMetadata &programMetadata, + const VaryingPacking &varyingPacking, + std::string *pixelHLSL, + std::string *vertexHLSL) const; - std::string generateGeometryShaderHLSL(int registers, - const ShaderD3D *fragmentShader, - const std::vector &packedVaryings) const; + std::string generateGeometryShaderPreamble(const VaryingPacking &varyingPacking) const; + + std::string generateGeometryShaderHLSL(gl::PrimitiveType primitiveType, + const gl::Data &data, + const gl::Program::Data &programData, + const std::string &preambleString) const; + + void getPixelShaderOutputKey(const gl::Data &data, + const gl::Program::Data &programData, + const ProgramD3DMetadata &metadata, + std::vector *outPixelShaderKey); private: RendererD3D *const mRenderer; - struct SemanticInfo; - - std::string getVaryingSemantic(bool pointSize) const; - SemanticInfo getSemanticInfo(int startRegisters, bool position, bool fragCoord, bool pointCoord, - bool pointSize, bool pixelShader) const; - std::string generateVaryingLinkHLSL(const SemanticInfo &info, const std::string &varyingHLSL) const; - std::string generateVaryingHLSL(const std::vector &varyings, - bool shaderUsesPointSize) const; - void storeUserLinkedVaryings(const std::vector &packedVaryings, - bool shaderUsesPointSize, - std::vector *linkedVaryings) const; - void storeBuiltinLinkedVaryings(const SemanticInfo &info, std::vector *linkedVaryings) const; - std::string generatePointSpriteHLSL(int registers, - const ShaderD3D *fragmentShader, - const std::vector &packedVaryings) const; + void generateVaryingLinkHLSL(ShaderType shaderType, + const VaryingPacking &varyingPacking, + std::stringstream &linkStream) const; + void generateVaryingHLSL(const VaryingPacking &varyingPacking, + std::stringstream &hlslStream) const; // Prepend an underscore static std::string decorateVariable(const std::string &name); @@ -123,6 +97,7 @@ class DynamicHLSL : angle::NonCopyable const sh::ShaderVariable &shaderAttrib) const; }; +std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize); } -#endif // LIBANGLE_RENDERER_D3D_DYNAMICHLSL_H_ +#endif // LIBANGLE_RENDERER_D3D_DYNAMICHLSL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL_unittest.cpp b/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL_unittest.cpp deleted file mode 100644 index ea7a331812da..000000000000 --- a/gfx/angle/src/libANGLE/renderer/d3d/DynamicHLSL_unittest.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright 2015 The ANGLE Project Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// DynamicHLSL unittests: -// Tests for DynamicHLSL and related classes. -// - -#include - -#include "libANGLE/renderer/d3d/DynamicHLSL.h" - -using namespace rx; - -namespace -{ - -TEST(PackedVarying, DefaultInitialization) -{ - sh::Varying defaultVarying; - PackedVarying pv(defaultVarying); - - EXPECT_EQ(&defaultVarying, pv.varying); - EXPECT_EQ(GL_INVALID_INDEX, pv.registerIndex); - EXPECT_EQ(0, pv.columnIndex); - EXPECT_FALSE(pv.vertexOnly); -} - -} // anonymous namespace diff --git a/gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp b/gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp index 7559b94b6aca..e8b1af312918 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp @@ -12,12 +12,9 @@ #include "libANGLE/histogram_macros.h" #include "third_party/trace_event/trace_event.h" -// Definitions local to the translation unit +#if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED namespace { - -#if ANGLE_SHADER_DEBUG_INFO == ANGLE_ENABLED - #ifdef CREATE_COMPILER_FLAG_INFO #undef CREATE_COMPILER_FLAG_INFO #endif @@ -78,10 +75,8 @@ bool IsCompilerFlagSet(UINT mask, UINT flag) return isFlagSet; } } - -#endif - -} +} // anonymous namespace +#endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED namespace rx { @@ -247,8 +242,9 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string { *outCompiledBlob = binary; -#if ANGLE_SHADER_DEBUG_INFO == ANGLE_ENABLED (*outDebugInfo) += "// COMPILER INPUT HLSL BEGIN\n\n" + hlsl + "\n// COMPILER INPUT HLSL END\n"; + +#if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED (*outDebugInfo) += "\n\n// ASSEMBLY BEGIN\n\n"; (*outDebugInfo) += "// Compiler configuration: " + configs[i].name + "\n// Flags:\n"; for (size_t fIx = 0; fIx < ArraySize(CompilerFlagInfos); ++fIx) @@ -279,7 +275,7 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string return error; } (*outDebugInfo) += "\n" + disassembly + "\n// ASSEMBLY END\n"; -#endif +#endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED return gl::Error(GL_NO_ERROR); } @@ -289,7 +285,8 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string return gl::Error(GL_OUT_OF_MEMORY, "HLSL compiler had an unexpected failure, result: 0x%X.", result); } - infoLog << "Warning: D3D shader compilation failed with " << configs[i].name << " flags."; + infoLog << "Warning: D3D shader compilation failed with " << configs[i].name << " flags. (" + << profile << ")"; if (i + 1 < configs.size()) { @@ -332,4 +329,4 @@ gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *d return gl::Error(GL_NO_ERROR); } -} +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp index 19cdbec72491..c18ffe1639bb 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp @@ -18,57 +18,82 @@ namespace rx { -static void ConvertIndices(GLenum sourceType, GLenum destinationType, const void *input, - GLsizei count, void *output) +namespace { + +template +void ConvertIndexArray(const void *input, + GLenum sourceType, + void *output, + GLenum destinationType, + GLsizei count, + bool usePrimitiveRestartFixedIndex) +{ + const InputT *in = static_cast(input); + DestT *out = static_cast(output); + + if (usePrimitiveRestartFixedIndex) + { + InputT srcRestartIndex = static_cast(gl::GetPrimitiveRestartIndex(sourceType)); + DestT destRestartIndex = static_cast(gl::GetPrimitiveRestartIndex(destinationType)); + for (GLsizei i = 0; i < count; i++) + { + out[i] = (in[i] == srcRestartIndex ? destRestartIndex : static_cast(in[i])); + } + } + else + { + for (GLsizei i = 0; i < count; i++) + { + out[i] = static_cast(in[i]); + } + } +} + +void ConvertIndices(GLenum sourceType, + GLenum destinationType, + const void *input, + GLsizei count, + void *output, + bool usePrimitiveRestartFixedIndex) +{ + if (sourceType == destinationType) + { + const gl::Type &typeInfo = gl::GetTypeInfo(destinationType); + memcpy(output, input, count * typeInfo.bytes); + return; + } + if (sourceType == GL_UNSIGNED_BYTE) { ASSERT(destinationType == GL_UNSIGNED_SHORT); - const GLubyte *in = static_cast(input); - GLushort *out = static_cast(output); - - for (GLsizei i = 0; i < count; i++) - { - out[i] = in[i]; - } - } - else if (sourceType == GL_UNSIGNED_INT) - { - ASSERT(destinationType == GL_UNSIGNED_INT); - memcpy(output, input, count * sizeof(GLuint)); + ConvertIndexArray(input, sourceType, output, destinationType, count, + usePrimitiveRestartFixedIndex); } else if (sourceType == GL_UNSIGNED_SHORT) { - if (destinationType == GL_UNSIGNED_SHORT) - { - memcpy(output, input, count * sizeof(GLushort)); - } - else if (destinationType == GL_UNSIGNED_INT) - { - const GLushort *in = static_cast(input); - GLuint *out = static_cast(output); - - for (GLsizei i = 0; i < count; i++) - { - out[i] = in[i]; - } - } - else UNREACHABLE(); + ASSERT(destinationType == GL_UNSIGNED_INT); + ConvertIndexArray(input, sourceType, output, destinationType, count, + usePrimitiveRestartFixedIndex); } else UNREACHABLE(); } -static gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, const GLvoid *data, - unsigned int count, GLenum srcType, GLenum dstType, - unsigned int *offset) +gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, + const GLvoid *data, + unsigned int count, + GLenum srcType, + GLenum dstType, + bool usePrimitiveRestartFixedIndex, + unsigned int *offset) { const gl::Type &dstTypeInfo = gl::GetTypeInfo(dstType); if (count > (std::numeric_limits::max() >> dstTypeInfo.bytesShift)) { return gl::Error(GL_OUT_OF_MEMORY, - "Reserving %u indices of %u bytes each exceeds the maximum buffer size.", - count, dstTypeInfo.bytes); + "Reserving %u indices of %u bytes each exceeds the maximum buffer size.", + count, dstTypeInfo.bytes); } unsigned int bufferSizeRequired = count << dstTypeInfo.bytesShift; @@ -85,7 +110,7 @@ static gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, const GLvoid return error; } - ConvertIndices(srcType, dstType, data, count, output); + ConvertIndices(srcType, dstType, data, count, output, usePrimitiveRestartFixedIndex); error = buffer->unmapBuffer(); if (error.isError()) @@ -96,6 +121,8 @@ static gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, const GLvoid return gl::Error(GL_NO_ERROR); } +} // anonymous namespace + IndexDataManager::IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass) : mFactory(factory), mRendererClass(rendererClass), @@ -118,9 +145,13 @@ IndexDataManager::~IndexDataManager() // When we have a buffer with an unsupported format (subcase b) then we need to do some translation: // we will start by falling back to streaming, and after a while will start using a static translated // copy of the index buffer. -gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl::Buffer *glBuffer, - const GLvoid *indices, TranslatedIndexData *translated, - SourceIndexData *sourceData) +gl::Error IndexDataManager::prepareIndexData(GLenum srcType, + GLsizei count, + gl::Buffer *glBuffer, + const GLvoid *indices, + TranslatedIndexData *translated, + SourceIndexData *sourceData, + bool primitiveRestartFixedIndexEnabled) { // Avoid D3D11's primitive restart index value // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx @@ -128,8 +159,14 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: translated->indexRange.vertexIndexCount < static_cast(count) || translated->indexRange.end == gl::GetPrimitiveRestartIndex(srcType); bool primitiveRestartWorkaround = mRendererClass == RENDERER_D3D11 && + !primitiveRestartFixedIndexEnabled && hasPrimitiveRestartIndex && srcType == GL_UNSIGNED_SHORT; + // We should never have to deal with MAX_UINT indices, since we restrict it via + // MAX_ELEMENT_INDEX. + ASSERT(!(mRendererClass == RENDERER_D3D11 && !primitiveRestartFixedIndexEnabled && + hasPrimitiveRestartIndex && srcType == GL_UNSIGNED_INT)); + const GLenum dstType = (srcType == GL_UNSIGNED_INT || primitiveRestartWorkaround) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; @@ -151,7 +188,8 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: if (glBuffer == nullptr) { translated->storage = nullptr; - return streamIndexData(indices, count, srcType, dstType, translated); + return streamIndexData(indices, count, srcType, dstType, primitiveRestartFixedIndexEnabled, + translated); } // Case 2: the indices are already in a buffer @@ -193,7 +231,7 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: if (staticBufferInitialized && !staticBufferUsable) { - buffer->invalidateStaticData(); + buffer->invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); staticBuffer = nullptr; } @@ -207,7 +245,8 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: } ASSERT(bufferData != nullptr); - error = streamIndexData(bufferData + offset, count, srcType, dstType, translated); + error = streamIndexData(bufferData + offset, count, srcType, dstType, + primitiveRestartFixedIndexEnabled, translated); if (error.isError()) { return error; @@ -227,8 +266,8 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: unsigned int convertCount = static_cast(buffer->getSize()) >> srcTypeInfo.bytesShift; - error = StreamInIndexBuffer(staticBuffer, bufferData, convertCount, - srcType, dstType, nullptr); + error = StreamInIndexBuffer(staticBuffer, bufferData, convertCount, srcType, dstType, + primitiveRestartFixedIndexEnabled, nullptr); if (error.isError()) { return error; @@ -245,8 +284,12 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl:: return gl::Error(GL_NO_ERROR); } -gl::Error IndexDataManager::streamIndexData(const GLvoid *data, unsigned int count, GLenum srcType, - GLenum dstType, TranslatedIndexData *translated) +gl::Error IndexDataManager::streamIndexData(const GLvoid *data, + unsigned int count, + GLenum srcType, + GLenum dstType, + bool usePrimitiveRestartFixedIndex, + TranslatedIndexData *translated) { const gl::Type &dstTypeInfo = gl::GetTypeInfo(dstType); @@ -259,7 +302,8 @@ gl::Error IndexDataManager::streamIndexData(const GLvoid *data, unsigned int cou ASSERT(indexBuffer != nullptr); unsigned int offset; - StreamInIndexBuffer(indexBuffer, data, count, srcType, dstType, &offset); + StreamInIndexBuffer(indexBuffer, data, count, srcType, dstType, usePrimitiveRestartFixedIndex, + &offset); translated->indexBuffer = indexBuffer->getIndexBuffer(); translated->serial = indexBuffer->getSerial(); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h index cd9c8b7110f1..c3741cfe3fc2 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/IndexDataManager.h @@ -63,13 +63,21 @@ class IndexDataManager : angle::NonCopyable explicit IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass); virtual ~IndexDataManager(); - gl::Error prepareIndexData(GLenum srcType, GLsizei count, gl::Buffer *glBuffer, - const GLvoid *indices, TranslatedIndexData *translated, - SourceIndexData *sourceData); + gl::Error prepareIndexData(GLenum srcType, + GLsizei count, + gl::Buffer *glBuffer, + const GLvoid *indices, + TranslatedIndexData *translated, + SourceIndexData *sourceData, + bool primitiveRestartFixedIndexEnabled); private: - gl::Error streamIndexData(const GLvoid *data, unsigned int count, GLenum srcType, - GLenum dstType, TranslatedIndexData *translated); + gl::Error streamIndexData(const GLvoid *data, + unsigned int count, + GLenum srcType, + GLenum dstType, + bool usePrimitiveRestartFixedIndex, + TranslatedIndexData *translated); gl::Error getStreamingIndexBuffer(GLenum destinationIndexType, IndexBufferInterface **outBuffer); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp index 446defd147dc..9dc4fc6a1b59 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp @@ -20,6 +20,7 @@ #include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/ShaderD3D.h" #include "libANGLE/renderer/d3d/ShaderExecutableD3D.h" +#include "libANGLE/renderer/d3d/VaryingPacking.h" #include "libANGLE/renderer/d3d/VertexDataManager.h" namespace rx @@ -38,14 +39,13 @@ gl::InputLayout GetDefaultInputLayoutFromShader(const gl::Shader *vertexShader) GLenum transposedType = gl::TransposeMatrixType(shaderAttr.type); for (size_t rowIndex = 0; - static_cast(rowIndex) < gl::VariableRowCount(transposedType); - ++rowIndex) + static_cast(rowIndex) < gl::VariableRowCount(transposedType); ++rowIndex) { GLenum componentType = gl::VariableComponentType(transposedType); - GLuint components = static_cast(gl::VariableColumnCount(transposedType)); + GLuint components = static_cast(gl::VariableColumnCount(transposedType)); bool pureInt = (componentType != GL_FLOAT); - gl::VertexFormatType defaultType = gl::GetVertexFormatType( - componentType, GL_FALSE, components, pureInt); + gl::VertexFormatType defaultType = + gl::GetVertexFormatType(componentType, GL_FALSE, components, pureInt); defaultLayout.push_back(defaultType); } @@ -55,7 +55,8 @@ gl::InputLayout GetDefaultInputLayoutFromShader(const gl::Shader *vertexShader) return defaultLayout; } -std::vector GetDefaultOutputLayoutFromShader(const std::vector &shaderOutputVars) +std::vector GetDefaultOutputLayoutFromShader( + const std::vector &shaderOutputVars) { std::vector defaultPixelOutput; @@ -90,14 +91,22 @@ struct AttributeSorter int indexA = (*originalIndices)[a]; int indexB = (*originalIndices)[b]; - if (indexA == -1) return false; - if (indexB == -1) return true; + if (indexA == -1) + return false; + if (indexB == -1) + return true; return (indexA < indexB); } const ProgramD3D::SemanticIndexArray *originalIndices; }; +// true if varying x has a higher priority in packing than y +bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y) +{ + return gl::CompareShaderVar(*x.varying, *y.varying); +} + std::vector MergeVaryings(const gl::Shader &vertexShader, const gl::Shader &fragmentShader, const std::vector &tfVaryings) @@ -118,7 +127,20 @@ std::vector MergeVaryings(const gl::Shader &vertexShader, { if (output.name == input.name) { - packedVaryings.push_back(PackedVarying(input)); + if (output.isStruct()) + { + ASSERT(!output.isArray()); + for (const auto &field : output.fields) + { + ASSERT(!field.isStruct() && !field.isArray()); + packedVaryings.push_back( + PackedVarying(field, input.interpolation, input.name)); + } + } + else + { + packedVaryings.push_back(PackedVarying(input, input.interpolation)); + } packed = true; break; } @@ -131,14 +153,22 @@ std::vector MergeVaryings(const gl::Shader &vertexShader, { if (tfVarying == output.name) { - packedVaryings.push_back(PackedVarying(output)); - packedVaryings.back().vertexOnly = true; + // Transform feedback for varying structs is underspecified. + // See Khronos bug 9856. + // TODO(jmadill): Figure out how to be spec-compliant here. + if (!output.isStruct()) + { + packedVaryings.push_back(PackedVarying(output, output.interpolation)); + packedVaryings.back().vertexOnly = true; + } break; } } } } + std::sort(packedVaryings.begin(), packedVaryings.end(), ComparePackedVarying); + return packedVaryings; } @@ -178,8 +208,119 @@ void GetUniformBlockInfo(const std::vector &fields, } } +template +bool TransposeMatrix(T *target, + const GLfloat *value, + int targetWidth, + int targetHeight, + int srcWidth, + int srcHeight) +{ + bool dirty = false; + int copyWidth = std::min(targetHeight, srcWidth); + int copyHeight = std::min(targetWidth, srcHeight); + + for (int x = 0; x < copyWidth; x++) + { + for (int y = 0; y < copyHeight; y++) + { + SetIfDirty(target + (x * targetWidth + y), static_cast(value[y * srcWidth + x]), + &dirty); + } + } + // clear unfilled right side + for (int y = 0; y < copyWidth; y++) + { + for (int x = copyHeight; x < targetWidth; x++) + { + SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); + } + } + // clear unfilled bottom. + for (int y = copyWidth; y < targetHeight; y++) + { + for (int x = 0; x < targetWidth; x++) + { + SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); + } + } + + return dirty; +} + +template +bool ExpandMatrix(T *target, + const GLfloat *value, + int targetWidth, + int targetHeight, + int srcWidth, + int srcHeight) +{ + bool dirty = false; + int copyWidth = std::min(targetWidth, srcWidth); + int copyHeight = std::min(targetHeight, srcHeight); + + for (int y = 0; y < copyHeight; y++) + { + for (int x = 0; x < copyWidth; x++) + { + SetIfDirty(target + (y * targetWidth + x), static_cast(value[y * srcWidth + x]), + &dirty); + } + } + // clear unfilled right side + for (int y = 0; y < copyHeight; y++) + { + for (int x = copyWidth; x < targetWidth; x++) + { + SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); + } + } + // clear unfilled bottom. + for (int y = copyHeight; y < targetHeight; y++) + { + for (int x = 0; x < targetWidth; x++) + { + SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); + } + } + + return dirty; +} + +gl::PrimitiveType GetGeometryShaderTypeFromDrawMode(GLenum drawMode) +{ + switch (drawMode) + { + // Uses the point sprite geometry shader. + case GL_POINTS: + return gl::PRIMITIVE_POINTS; + + // All line drawing uses the same geometry shader. + case GL_LINES: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + return gl::PRIMITIVE_LINES; + + // The triangle fan primitive is emulated with strips in D3D11. + case GL_TRIANGLES: + case GL_TRIANGLE_FAN: + return gl::PRIMITIVE_TRIANGLES; + + // Special case for triangle strips. + case GL_TRIANGLE_STRIP: + return gl::PRIMITIVE_TRIANGLE_STRIP; + + default: + UNREACHABLE(); + return gl::PRIMITIVE_TYPE_MAX; + } +} + } // anonymous namespace +// D3DUniform Implementation + D3DUniform::D3DUniform(GLenum typeIn, const std::string &nameIn, unsigned int arraySizeIn, @@ -228,12 +369,121 @@ bool D3DUniform::isReferencedByFragmentShader() const return psRegisterIndex != GL_INVALID_INDEX; } +// D3DVarying Implementation + +D3DVarying::D3DVarying() : semanticIndex(0), componentCount(0), outputSlot(0) +{ +} + +D3DVarying::D3DVarying(const std::string &semanticNameIn, + unsigned int semanticIndexIn, + unsigned int componentCountIn, + unsigned int outputSlotIn) + : semanticName(semanticNameIn), + semanticIndex(semanticIndexIn), + componentCount(componentCountIn), + outputSlot(outputSlotIn) +{ +} + +// ProgramD3DMetadata Implementation + +ProgramD3DMetadata::ProgramD3DMetadata(int rendererMajorShaderModel, + const std::string &shaderModelSuffix, + bool usesInstancedPointSpriteEmulation, + const ShaderD3D *vertexShader, + const ShaderD3D *fragmentShader) + : mRendererMajorShaderModel(rendererMajorShaderModel), + mShaderModelSuffix(shaderModelSuffix), + mUsesInstancedPointSpriteEmulation(usesInstancedPointSpriteEmulation), + mVertexShader(vertexShader), + mFragmentShader(fragmentShader) +{ +} + +int ProgramD3DMetadata::getRendererMajorShaderModel() const +{ + return mRendererMajorShaderModel; +} + +bool ProgramD3DMetadata::usesBroadcast(const gl::Data &data) const +{ + return (mFragmentShader->usesFragColor() && data.clientVersion < 3); +} + +bool ProgramD3DMetadata::usesFragDepth(const gl::Program::Data &programData) const +{ + // TODO(jmadill): Rename this or check if we need it for version 300 + return (getMajorShaderVersion() < 300 && mFragmentShader->usesFragDepth()); +} + +bool ProgramD3DMetadata::usesPointCoord() const +{ + return mFragmentShader->usesPointCoord(); +} + +bool ProgramD3DMetadata::usesFragCoord() const +{ + return mFragmentShader->usesFragCoord(); +} + +bool ProgramD3DMetadata::usesPointSize() const +{ + return mVertexShader->usesPointSize(); +} + +bool ProgramD3DMetadata::usesInsertedPointCoordValue() const +{ + return !usesPointSize() && usesPointCoord() && mRendererMajorShaderModel >= 4; +} + +bool ProgramD3DMetadata::addsPointCoordToVertexShader() const +{ + // Instanced PointSprite emulation requires that gl_PointCoord is present in the vertex shader + // VS_OUTPUT structure to ensure compatibility with the generated PS_INPUT of the pixel shader. + // GeometryShader PointSprite emulation does not require this additional entry because the + // GS_OUTPUT of the Geometry shader contains the pointCoord value and already matches the + // PS_INPUT of the generated pixel shader. The Geometry Shader point sprite implementation needs + // gl_PointSize to be in VS_OUTPUT and GS_INPUT. Instanced point sprites doesn't need + // gl_PointSize in VS_OUTPUT. + return (mUsesInstancedPointSpriteEmulation && usesPointCoord()) || + usesInsertedPointCoordValue(); +} + +bool ProgramD3DMetadata::usesTransformFeedbackGLPosition() const +{ + // gl_Position only needs to be outputted from the vertex shader if transform feedback is + // active. This isn't supported on D3D11 Feature Level 9_3, so we don't output gl_Position from + // the vertex shader in this case. This saves us 1 output vector. + return !(mRendererMajorShaderModel >= 4 && mShaderModelSuffix != ""); +} + +bool ProgramD3DMetadata::usesSystemValuePointSize() const +{ + return !mUsesInstancedPointSpriteEmulation && usesPointSize(); +} + +bool ProgramD3DMetadata::usesMultipleFragmentOuts() const +{ + return mFragmentShader->usesMultipleRenderTargets(); +} + +GLint ProgramD3DMetadata::getMajorShaderVersion() const +{ + return mVertexShader->getData().getShaderVersion(); +} + +const ShaderD3D *ProgramD3DMetadata::getFragmentShader() const +{ + return mFragmentShader; +} + +// ProgramD3D Implementation + ProgramD3D::VertexExecutable::VertexExecutable(const gl::InputLayout &inputLayout, const Signature &signature, ShaderExecutableD3D *shaderExecutable) - : mInputs(inputLayout), - mSignature(signature), - mShaderExecutable(shaderExecutable) + : mInputs(inputLayout), mSignature(signature), mShaderExecutable(shaderExecutable) { } @@ -281,8 +531,7 @@ bool ProgramD3D::VertexExecutable::matchesSignature(const Signature &signature) ProgramD3D::PixelExecutable::PixelExecutable(const std::vector &outputSignature, ShaderExecutableD3D *shaderExecutable) - : mOutputSignature(outputSignature), - mShaderExecutable(shaderExecutable) + : mOutputSignature(outputSignature), mShaderExecutable(shaderExecutable) { } @@ -301,8 +550,9 @@ ProgramD3D::ProgramD3D(const gl::Program::Data &data, RendererD3D *renderer) : ProgramImpl(data), mRenderer(renderer), mDynamicHLSL(NULL), - mGeometryExecutable(NULL), + mGeometryExecutables(gl::PRIMITIVE_TYPE_MAX, nullptr), mUsesPointSize(false), + mUsesFlatInterpolation(false), mVertexUniformStorage(NULL), mFragmentUniformStorage(NULL), mUsedVertexSamplerRange(0), @@ -324,8 +574,13 @@ bool ProgramD3D::usesPointSpriteEmulation() const return mUsesPointSize && mRenderer->getMajorShaderModel() >= 4; } -bool ProgramD3D::usesGeometryShader() const +bool ProgramD3D::usesGeometryShader(GLenum drawMode) const { + if (drawMode != GL_POINTS) + { + return mUsesFlatInterpolation; + } + return usesPointSpriteEmulation() && !usesInstancedPointSpriteEmulation(); } @@ -334,30 +589,34 @@ bool ProgramD3D::usesInstancedPointSpriteEmulation() const return mRenderer->getWorkarounds().useInstancedPointSpriteEmulation; } -GLint ProgramD3D::getSamplerMapping(gl::SamplerType type, unsigned int samplerIndex, const gl::Caps &caps) const +GLint ProgramD3D::getSamplerMapping(gl::SamplerType type, + unsigned int samplerIndex, + const gl::Caps &caps) const { GLint logicalTextureUnit = -1; switch (type) { - case gl::SAMPLER_PIXEL: - ASSERT(samplerIndex < caps.maxTextureImageUnits); - if (samplerIndex < mSamplersPS.size() && mSamplersPS[samplerIndex].active) - { - logicalTextureUnit = mSamplersPS[samplerIndex].logicalTextureUnit; - } - break; - case gl::SAMPLER_VERTEX: - ASSERT(samplerIndex < caps.maxVertexTextureImageUnits); - if (samplerIndex < mSamplersVS.size() && mSamplersVS[samplerIndex].active) - { - logicalTextureUnit = mSamplersVS[samplerIndex].logicalTextureUnit; - } - break; - default: UNREACHABLE(); + case gl::SAMPLER_PIXEL: + ASSERT(samplerIndex < caps.maxTextureImageUnits); + if (samplerIndex < mSamplersPS.size() && mSamplersPS[samplerIndex].active) + { + logicalTextureUnit = mSamplersPS[samplerIndex].logicalTextureUnit; + } + break; + case gl::SAMPLER_VERTEX: + ASSERT(samplerIndex < caps.maxVertexTextureImageUnits); + if (samplerIndex < mSamplersVS.size() && mSamplersVS[samplerIndex].active) + { + logicalTextureUnit = mSamplersVS[samplerIndex].logicalTextureUnit; + } + break; + default: + UNREACHABLE(); } - if (logicalTextureUnit >= 0 && logicalTextureUnit < static_cast(caps.maxCombinedTextureImageUnits)) + if (logicalTextureUnit >= 0 && + logicalTextureUnit < static_cast(caps.maxCombinedTextureImageUnits)) { return logicalTextureUnit; } @@ -371,15 +630,16 @@ GLenum ProgramD3D::getSamplerTextureType(gl::SamplerType type, unsigned int samp { switch (type) { - case gl::SAMPLER_PIXEL: - ASSERT(samplerIndex < mSamplersPS.size()); - ASSERT(mSamplersPS[samplerIndex].active); - return mSamplersPS[samplerIndex].textureType; - case gl::SAMPLER_VERTEX: - ASSERT(samplerIndex < mSamplersVS.size()); - ASSERT(mSamplersVS[samplerIndex].active); - return mSamplersVS[samplerIndex].textureType; - default: UNREACHABLE(); + case gl::SAMPLER_PIXEL: + ASSERT(samplerIndex < mSamplersPS.size()); + ASSERT(mSamplersPS[samplerIndex].active); + return mSamplersPS[samplerIndex].textureType; + case gl::SAMPLER_VERTEX: + ASSERT(samplerIndex < mSamplersVS.size()); + ASSERT(mSamplersVS[samplerIndex].active); + return mSamplersVS[samplerIndex].textureType; + default: + UNREACHABLE(); } return GL_TEXTURE_2D; @@ -389,13 +649,13 @@ GLint ProgramD3D::getUsedSamplerRange(gl::SamplerType type) const { switch (type) { - case gl::SAMPLER_PIXEL: - return mUsedPixelSamplerRange; - case gl::SAMPLER_VERTEX: - return mUsedVertexSamplerRange; - default: - UNREACHABLE(); - return 0; + case gl::SAMPLER_PIXEL: + return mUsedPixelSamplerRange; + case gl::SAMPLER_VERTEX: + return mUsedVertexSamplerRange; + default: + UNREACHABLE(); + return 0; } } @@ -458,8 +718,9 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) { reset(); - DeviceIdentifier binaryDeviceIdentifier = { 0 }; - stream->readBytes(reinterpret_cast(&binaryDeviceIdentifier), sizeof(DeviceIdentifier)); + DeviceIdentifier binaryDeviceIdentifier = {0}; + stream->readBytes(reinterpret_cast(&binaryDeviceIdentifier), + sizeof(DeviceIdentifier)); DeviceIdentifier identifier = mRenderer->getAdapterIdentifier(); if (memcmp(&identifier, &binaryDeviceIdentifier, sizeof(DeviceIdentifier)) != 0) @@ -543,30 +804,32 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) mD3DUniformBlocks.push_back(uniformBlock); } - const unsigned int transformFeedbackVaryingCount = stream->readInt(); - mTransformFeedbackLinkedVaryings.resize(transformFeedbackVaryingCount); - for (unsigned int varyingIndex = 0; varyingIndex < transformFeedbackVaryingCount; varyingIndex++) + const unsigned int streamOutVaryingCount = stream->readInt(); + mStreamOutVaryings.resize(streamOutVaryingCount); + for (unsigned int varyingIndex = 0; varyingIndex < streamOutVaryingCount; ++varyingIndex) { - gl::LinkedVarying &varying = mTransformFeedbackLinkedVaryings[varyingIndex]; + D3DVarying *varying = &mStreamOutVaryings[varyingIndex]; - stream->readString(&varying.name); - stream->readInt(&varying.type); - stream->readInt(&varying.size); - stream->readString(&varying.semanticName); - stream->readInt(&varying.semanticIndex); - stream->readInt(&varying.semanticIndexCount); + stream->readString(&varying->semanticName); + stream->readInt(&varying->semanticIndex); + stream->readInt(&varying->componentCount); + stream->readInt(&varying->outputSlot); } stream->readString(&mVertexHLSL); - stream->readBytes(reinterpret_cast(&mVertexWorkarounds), sizeof(D3DCompilerWorkarounds)); + stream->readBytes(reinterpret_cast(&mVertexWorkarounds), + sizeof(D3DCompilerWorkarounds)); stream->readString(&mPixelHLSL); - stream->readBytes(reinterpret_cast(&mPixelWorkarounds), sizeof(D3DCompilerWorkarounds)); + stream->readBytes(reinterpret_cast(&mPixelWorkarounds), + sizeof(D3DCompilerWorkarounds)); stream->readBool(&mUsesFragDepth); stream->readBool(&mUsesPointSize); + stream->readBool(&mUsesFlatInterpolation); const size_t pixelShaderKeySize = stream->readInt(); mPixelShaderKey.resize(pixelShaderKeySize); - for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKeySize; pixelShaderKeyIndex++) + for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKeySize; + pixelShaderKeyIndex++) { stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].type); stream->readString(&mPixelShaderKey[pixelShaderKeyIndex].name); @@ -574,10 +837,13 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].outputIndex); } - const unsigned char* binary = reinterpret_cast(stream->data()); + stream->readString(&mGeometryShaderPreamble); + + const unsigned char *binary = reinterpret_cast(stream->data()); const unsigned int vertexShaderCount = stream->readInt(); - for (unsigned int vertexShaderIndex = 0; vertexShaderIndex < vertexShaderCount; vertexShaderIndex++) + for (unsigned int vertexShaderIndex = 0; vertexShaderIndex < vertexShaderCount; + vertexShaderIndex++) { size_t inputLayoutSize = stream->readInt(); gl::InputLayout inputLayout(inputLayoutSize, gl::VERTEX_FORMAT_INVALID); @@ -587,13 +853,13 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) inputLayout[inputIndex] = stream->readInt(); } - unsigned int vertexShaderSize = stream->readInt(); + unsigned int vertexShaderSize = stream->readInt(); const unsigned char *vertexShaderFunction = binary + stream->offset(); ShaderExecutableD3D *shaderExecutable = nullptr; gl::Error error = mRenderer->loadExecutable( - vertexShaderFunction, vertexShaderSize, SHADER_VERTEX, mTransformFeedbackLinkedVaryings, + vertexShaderFunction, vertexShaderSize, SHADER_VERTEX, mStreamOutVaryings, (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), &shaderExecutable); if (error.isError()) { @@ -611,7 +877,8 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) VertexExecutable::getSignature(mRenderer, inputLayout, &signature); // add new binary - mVertexExecutables.push_back(new VertexExecutable(inputLayout, signature, shaderExecutable)); + mVertexExecutables.push_back( + new VertexExecutable(inputLayout, signature, shaderExecutable)); stream->skip(vertexShaderSize); } @@ -626,12 +893,12 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) stream->readInt(&outputs[outputIndex]); } - const size_t pixelShaderSize = stream->readInt(); + const size_t pixelShaderSize = stream->readInt(); const unsigned char *pixelShaderFunction = binary + stream->offset(); ShaderExecutableD3D *shaderExecutable = nullptr; gl::Error error = mRenderer->loadExecutable( - pixelShaderFunction, pixelShaderSize, SHADER_PIXEL, mTransformFeedbackLinkedVaryings, + pixelShaderFunction, pixelShaderSize, SHADER_PIXEL, mStreamOutVaryings, (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), &shaderExecutable); if (error.isError()) { @@ -650,21 +917,28 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) stream->skip(pixelShaderSize); } - unsigned int geometryShaderSize = stream->readInt(); - - if (geometryShaderSize > 0) + for (unsigned int geometryExeIndex = 0; geometryExeIndex < gl::PRIMITIVE_TYPE_MAX; + ++geometryExeIndex) { + unsigned int geometryShaderSize = stream->readInt(); + if (geometryShaderSize == 0) + { + mGeometryExecutables[geometryExeIndex] = nullptr; + continue; + } + const unsigned char *geometryShaderFunction = binary + stream->offset(); - gl::Error error = mRenderer->loadExecutable( - geometryShaderFunction, geometryShaderSize, SHADER_GEOMETRY, - mTransformFeedbackLinkedVaryings, - (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), &mGeometryExecutable); + bool splitAttribs = (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS); + + gl::Error error = mRenderer->loadExecutable( + geometryShaderFunction, geometryShaderSize, SHADER_GEOMETRY, mStreamOutVaryings, + splitAttribs, &mGeometryExecutables[geometryExeIndex]); if (error.isError()) { return LinkResult(false, error); } - if (!mGeometryExecutable) + if (!mGeometryExecutables[geometryExeIndex]) { infoLog << "Could not create geometry shader."; return LinkResult(false, gl::Error(GL_NO_ERROR)); @@ -681,9 +955,11 @@ LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) { // Output the DeviceIdentifier before we output any shader code - // When we load the binary again later, we can validate the device identifier before trying to compile any HLSL + // When we load the binary again later, we can validate the device identifier before trying to + // compile any HLSL DeviceIdentifier binaryIdentifier = mRenderer->getAdapterIdentifier(); - stream->writeBytes(reinterpret_cast(&binaryIdentifier), sizeof(DeviceIdentifier)); + stream->writeBytes(reinterpret_cast(&binaryIdentifier), + sizeof(DeviceIdentifier)); stream->writeInt(ANGLE_COMPILE_OPTIMIZATION_LEVEL); @@ -729,29 +1005,29 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) stream->writeInt(uniformBlock.vsRegisterIndex); } - stream->writeInt(mTransformFeedbackLinkedVaryings.size()); - for (size_t i = 0; i < mTransformFeedbackLinkedVaryings.size(); i++) + stream->writeInt(mStreamOutVaryings.size()); + for (const auto &varying : mStreamOutVaryings) { - const gl::LinkedVarying &varying = mTransformFeedbackLinkedVaryings[i]; - - stream->writeString(varying.name); - stream->writeInt(varying.type); - stream->writeInt(varying.size); stream->writeString(varying.semanticName); stream->writeInt(varying.semanticIndex); - stream->writeInt(varying.semanticIndexCount); + stream->writeInt(varying.componentCount); + stream->writeInt(varying.outputSlot); } stream->writeString(mVertexHLSL); - stream->writeBytes(reinterpret_cast(&mVertexWorkarounds), sizeof(D3DCompilerWorkarounds)); + stream->writeBytes(reinterpret_cast(&mVertexWorkarounds), + sizeof(D3DCompilerWorkarounds)); stream->writeString(mPixelHLSL); - stream->writeBytes(reinterpret_cast(&mPixelWorkarounds), sizeof(D3DCompilerWorkarounds)); + stream->writeBytes(reinterpret_cast(&mPixelWorkarounds), + sizeof(D3DCompilerWorkarounds)); stream->writeInt(mUsesFragDepth); stream->writeInt(mUsesPointSize); + stream->writeInt(mUsesFlatInterpolation); const std::vector &pixelShaderKey = mPixelShaderKey; stream->writeInt(pixelShaderKey.size()); - for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKey.size(); pixelShaderKeyIndex++) + for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKey.size(); + pixelShaderKeyIndex++) { const PixelShaderOutputVariable &variable = pixelShaderKey[pixelShaderKeyIndex]; stream->writeInt(variable.type); @@ -760,8 +1036,11 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) stream->writeInt(variable.outputIndex); } + stream->writeString(mGeometryShaderPreamble); + stream->writeInt(mVertexExecutables.size()); - for (size_t vertexExecutableIndex = 0; vertexExecutableIndex < mVertexExecutables.size(); vertexExecutableIndex++) + for (size_t vertexExecutableIndex = 0; vertexExecutableIndex < mVertexExecutables.size(); + vertexExecutableIndex++) { VertexExecutable *vertexExecutable = mVertexExecutables[vertexExecutableIndex]; @@ -781,7 +1060,8 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) } stream->writeInt(mPixelExecutables.size()); - for (size_t pixelExecutableIndex = 0; pixelExecutableIndex < mPixelExecutables.size(); pixelExecutableIndex++) + for (size_t pixelExecutableIndex = 0; pixelExecutableIndex < mPixelExecutables.size(); + pixelExecutableIndex++) { PixelExecutable *pixelExecutable = mPixelExecutables[pixelExecutableIndex]; @@ -799,24 +1079,30 @@ gl::Error ProgramD3D::save(gl::BinaryOutputStream *stream) stream->writeBytes(pixelBlob, pixelShaderSize); } - size_t geometryShaderSize = (mGeometryExecutable != NULL) ? mGeometryExecutable->getLength() : 0; - stream->writeInt(geometryShaderSize); - - if (mGeometryExecutable != NULL && geometryShaderSize > 0) + for (const ShaderExecutableD3D *geometryExe : mGeometryExecutables) { - const uint8_t *geometryBlob = mGeometryExecutable->getFunction(); - stream->writeBytes(geometryBlob, geometryShaderSize); + if (geometryExe == nullptr) + { + stream->writeInt(0); + continue; + } + + size_t geometryShaderSize = geometryExe->getLength(); + stream->writeInt(geometryShaderSize); + stream->writeBytes(geometryExe->getFunction(), geometryShaderSize); } return gl::Error(GL_NO_ERROR); } -gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, ShaderExecutableD3D **outExecutable) +gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, + ShaderExecutableD3D **outExecutable) { mPixelShaderOutputFormatCache.clear(); const FramebufferD3D *fboD3D = GetImplAs(fbo); - const gl::AttachmentList &colorbuffers = fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds()); + const gl::AttachmentList &colorbuffers = + fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds()); for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) { @@ -824,7 +1110,9 @@ gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fb if (colorbuffer) { - mPixelShaderOutputFormatCache.push_back(colorbuffer->getBinding() == GL_BACK ? GL_COLOR_ATTACHMENT0 : colorbuffer->getBinding()); + mPixelShaderOutputFormatCache.push_back(colorbuffer->getBinding() == GL_BACK + ? GL_COLOR_ATTACHMENT0 + : colorbuffer->getBinding()); } else { @@ -848,8 +1136,8 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vectorgeneratePixelShaderForOutputSignature(mPixelHLSL, mPixelShaderKey, mUsesFragDepth, - outputSignature); + std::string finalPixelHLSL = mDynamicHLSL->generatePixelShaderForOutputSignature( + mPixelHLSL, mPixelShaderKey, mUsesFragDepth, outputSignature); // Generate new pixel executable ShaderExecutableD3D *pixelExecutable = NULL; @@ -858,7 +1146,7 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vectorcompileToExecutable( - *currentInfoLog, finalPixelHLSL, SHADER_PIXEL, mTransformFeedbackLinkedVaryings, + *currentInfoLog, finalPixelHLSL, SHADER_PIXEL, mStreamOutVaryings, (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mPixelWorkarounds, &pixelExecutable); if (error.isError()) @@ -907,7 +1195,7 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog; gl::Error error = mRenderer->compileToExecutable( - *currentInfoLog, finalVertexHLSL, SHADER_VERTEX, mTransformFeedbackLinkedVaryings, + *currentInfoLog, finalVertexHLSL, SHADER_VERTEX, mStreamOutVaryings, (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mVertexWorkarounds, &vertexExecutable); if (error.isError()) @@ -917,7 +1205,8 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i if (vertexExecutable) { - mVertexExecutables.push_back(new VertexExecutable(inputLayout, mCachedVertexSignature, vertexExecutable)); + mVertexExecutables.push_back( + new VertexExecutable(inputLayout, mCachedVertexSignature, vertexExecutable)); } else if (!infoLog) { @@ -930,53 +1219,96 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i return gl::Error(GL_NO_ERROR); } -LinkResult ProgramD3D::compileProgramExecutables(gl::InfoLog &infoLog, - int registers, - const std::vector &packedVaryings) +gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::Data &data, + GLenum drawMode, + ShaderExecutableD3D **outExecutable, + gl::InfoLog *infoLog) { - const ShaderD3D *fragmentShaderD3D = GetImplAs(mData.getAttachedFragmentShader()); + if (outExecutable) + { + *outExecutable = nullptr; + } + // We only uses a geometry shader for point sprite emulation, or for fixing the provoking + // vertex problem. Otherwise, return a null shader. + if (drawMode != GL_POINTS && !mUsesFlatInterpolation) + { + return gl::Error(GL_NO_ERROR); + } + + gl::PrimitiveType geometryShaderType = GetGeometryShaderTypeFromDrawMode(drawMode); + + if (mGeometryExecutables[geometryShaderType] != nullptr) + { + if (outExecutable) + { + *outExecutable = mGeometryExecutables[geometryShaderType]; + } + return gl::Error(GL_NO_ERROR); + } + + std::string geometryHLSL = mDynamicHLSL->generateGeometryShaderHLSL( + geometryShaderType, data, mData, mGeometryShaderPreamble); + + gl::InfoLog tempInfoLog; + gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog; + + gl::Error error = mRenderer->compileToExecutable( + *currentInfoLog, geometryHLSL, SHADER_GEOMETRY, mStreamOutVaryings, + (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), D3DCompilerWorkarounds(), + &mGeometryExecutables[geometryShaderType]); + + if (!infoLog && error.isError()) + { + std::vector tempCharBuffer(tempInfoLog.getLength() + 3); + tempInfoLog.getLog(static_cast(tempInfoLog.getLength()), NULL, &tempCharBuffer[0]); + ERR("Error compiling dynamic geometry executable:\n%s\n", &tempCharBuffer[0]); + } + + if (outExecutable) + { + *outExecutable = mGeometryExecutables[geometryShaderType]; + } + return error; +} + +LinkResult ProgramD3D::compileProgramExecutables(const gl::Data &data, gl::InfoLog &infoLog) +{ const gl::InputLayout &defaultInputLayout = GetDefaultInputLayoutFromShader(mData.getAttachedVertexShader()); ShaderExecutableD3D *defaultVertexExecutable = NULL; - gl::Error error = getVertexExecutableForInputLayout(defaultInputLayout, &defaultVertexExecutable, &infoLog); + gl::Error error = + getVertexExecutableForInputLayout(defaultInputLayout, &defaultVertexExecutable, &infoLog); if (error.isError()) { return LinkResult(false, error); } - std::vector defaultPixelOutput = GetDefaultOutputLayoutFromShader(getPixelShaderKey()); + std::vector defaultPixelOutput = GetDefaultOutputLayoutFromShader(getPixelShaderKey()); ShaderExecutableD3D *defaultPixelExecutable = NULL; - error = getPixelExecutableForOutputLayout(defaultPixelOutput, &defaultPixelExecutable, &infoLog); + error = + getPixelExecutableForOutputLayout(defaultPixelOutput, &defaultPixelExecutable, &infoLog); if (error.isError()) { return LinkResult(false, error); } - if (usesGeometryShader()) + // Auto-generate the geometry shader here, if we expect to be using point rendering in D3D11. + ShaderExecutableD3D *pointGS = nullptr; + if (usesGeometryShader(GL_POINTS)) { - std::string geometryHLSL = - mDynamicHLSL->generateGeometryShaderHLSL(registers, fragmentShaderD3D, packedVaryings); - - error = mRenderer->compileToExecutable( - infoLog, geometryHLSL, SHADER_GEOMETRY, mTransformFeedbackLinkedVaryings, - (mData.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), - D3DCompilerWorkarounds(), &mGeometryExecutable); - if (error.isError()) - { - return LinkResult(false, error); - } + getGeometryExecutableForPrimitiveType(data, GL_POINTS, &pointGS, &infoLog); } -#if ANGLE_SHADER_DEBUG_INFO == ANGLE_ENABLED const ShaderD3D *vertexShaderD3D = GetImplAs(mData.getAttachedVertexShader()); - if (usesGeometryShader() && mGeometryExecutable) + if (usesGeometryShader(GL_POINTS) && pointGS) { - // Geometry shaders are currently only used internally, so there is no corresponding shader object at the interface level - // For now the geometry shader debug info is pre-pended to the vertex shader, this is a bit of a clutch + // Geometry shaders are currently only used internally, so there is no corresponding shader + // object at the interface level. For now the geometry shader debug info is prepended to + // the vertex shader. vertexShaderD3D->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n"); - vertexShaderD3D->appendDebugInfo(mGeometryExecutable->getDebugInfo()); + vertexShaderD3D->appendDebugInfo(pointGS->getDebugInfo()); vertexShaderD3D->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n"); } @@ -987,11 +1319,13 @@ LinkResult ProgramD3D::compileProgramExecutables(gl::InfoLog &infoLog, if (defaultPixelExecutable) { + const ShaderD3D *fragmentShaderD3D = + GetImplAs(mData.getAttachedFragmentShader()); fragmentShaderD3D->appendDebugInfo(defaultPixelExecutable->getDebugInfo()); } -#endif - bool linkSuccess = (defaultVertexExecutable && defaultPixelExecutable && (!usesGeometryShader() || mGeometryExecutable)); + bool linkSuccess = (defaultVertexExecutable && defaultPixelExecutable && + (!usesGeometryShader(GL_POINTS) || pointGS)); return LinkResult(linkSuccess, gl::Error(GL_NO_ERROR)); } @@ -1018,10 +1352,7 @@ LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog) mSamplersVS.resize(data.caps->maxVertexTextureImageUnits); mSamplersPS.resize(data.caps->maxTextureImageUnits); - mVertexHLSL = vertexShader->getTranslatedSource(); vertexShaderD3D->generateWorkarounds(&mVertexWorkarounds); - - mPixelHLSL = fragmentShader->getTranslatedSource(); fragmentShaderD3D->generateWorkarounds(&mPixelWorkarounds); if (mRenderer->getRendererLimitations().noFrontFacingSupport) @@ -1037,31 +1368,70 @@ LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog) MergeVaryings(*vertexShader, *fragmentShader, mData.getTransformFeedbackVaryingNames()); // Map the varyings to the register file - int registers = mDynamicHLSL->packVaryings(infoLog, &packedVaryings, - mData.getTransformFeedbackVaryingNames()); - - if (registers < 0) + VaryingPacking varyingPacking(data.caps->maxVaryingVectors); + if (!varyingPacking.packVaryings(infoLog, packedVaryings, + mData.getTransformFeedbackVaryingNames())) { return LinkResult(false, gl::Error(GL_NO_ERROR)); } - std::vector linkedVaryings; - if (!mDynamicHLSL->generateShaderLinkHLSL(data, mData, infoLog, registers, mPixelHLSL, - mVertexHLSL, packedVaryings, &linkedVaryings, - &mPixelShaderKey, &mUsesFragDepth)) + ProgramD3DMetadata metadata(mRenderer->getMajorShaderModel(), mRenderer->getShaderModelSuffix(), + usesInstancedPointSpriteEmulation(), vertexShaderD3D, + fragmentShaderD3D); + + varyingPacking.enableBuiltins(SHADER_VERTEX, metadata); + varyingPacking.enableBuiltins(SHADER_PIXEL, metadata); + + if (static_cast(varyingPacking.getRegisterCount()) > data.caps->maxVaryingVectors) + { + infoLog << "No varying registers left to support gl_FragCoord/gl_PointCoord"; + return LinkResult(false, gl::Error(GL_NO_ERROR)); + } + + // TODO(jmadill): Implement more sophisticated component packing in D3D9. + // We can fail here because we use one semantic per GLSL varying. D3D11 can pack varyings + // intelligently, but D3D9 assumes one semantic per register. + if (mRenderer->getRendererClass() == RENDERER_D3D9 && + varyingPacking.getMaxSemanticIndex() > data.caps->maxVaryingVectors) + { + infoLog << "Cannot pack these varyings on D3D9."; + return LinkResult(false, gl::Error(GL_NO_ERROR)); + } + + if (!mDynamicHLSL->generateShaderLinkHLSL(data, mData, metadata, varyingPacking, &mPixelHLSL, + &mVertexHLSL)) { return LinkResult(false, gl::Error(GL_NO_ERROR)); } mUsesPointSize = vertexShaderD3D->usesPointSize(); + mDynamicHLSL->getPixelShaderOutputKey(data, mData, metadata, &mPixelShaderKey); + mUsesFragDepth = metadata.usesFragDepth(mData); + + // Cache if we use flat shading + mUsesFlatInterpolation = false; + for (const auto &varying : packedVaryings) + { + if (varying.interpolation == sh::INTERPOLATION_FLAT) + { + mUsesFlatInterpolation = true; + break; + } + } + + if (mRenderer->getMajorShaderModel() >= 4) + { + varyingPacking.enableBuiltins(SHADER_GEOMETRY, metadata); + mGeometryShaderPreamble = mDynamicHLSL->generateGeometryShaderPreamble(varyingPacking); + } initSemanticIndex(); defineUniformsAndAssignRegisters(); - gatherTransformFeedbackVaryings(linkedVaryings); + gatherTransformFeedbackVaryings(varyingPacking); - LinkResult result = compileProgramExecutables(infoLog, registers, packedVaryings); + LinkResult result = compileProgramExecutables(data, infoLog); if (result.error.isError() || !result.linkSuccess) { infoLog << "Failed to create D3D shaders."; @@ -1145,7 +1515,7 @@ void ProgramD3D::assignUniformBlockRegisters() void ProgramD3D::initializeUniformStorage() { // Compute total default block size - unsigned int vertexRegisters = 0; + unsigned int vertexRegisters = 0; unsigned int fragmentRegisters = 0; for (const D3DUniform *d3dUniform : mD3DUniforms) { @@ -1164,15 +1534,15 @@ void ProgramD3D::initializeUniformStorage() } } - mVertexUniformStorage = mRenderer->createUniformStorage(vertexRegisters * 16u); + mVertexUniformStorage = mRenderer->createUniformStorage(vertexRegisters * 16u); mFragmentUniformStorage = mRenderer->createUniformStorage(fragmentRegisters * 16u); } -gl::Error ProgramD3D::applyUniforms() +gl::Error ProgramD3D::applyUniforms(GLenum drawMode) { updateSamplerMapping(); - gl::Error error = mRenderer->applyUniforms(*this, mD3DUniforms); + gl::Error error = mRenderer->applyUniforms(*this, drawMode, mD3DUniforms); if (error.isError()) { return error; @@ -1257,7 +1627,7 @@ void ProgramD3D::dirtyAllUniforms() } } -void ProgramD3D::setUniform1fv(GLint location, GLsizei count, const GLfloat* v) +void ProgramD3D::setUniform1fv(GLint location, GLsizei count, const GLfloat *v) { setUniform(location, count, v, GL_FLOAT); } @@ -1277,47 +1647,74 @@ void ProgramD3D::setUniform4fv(GLint location, GLsizei count, const GLfloat *v) setUniform(location, count, v, GL_FLOAT_VEC4); } -void ProgramD3D::setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<2, 2>(location, count, transpose, value, GL_FLOAT_MAT2); } -void ProgramD3D::setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<3, 3>(location, count, transpose, value, GL_FLOAT_MAT3); } -void ProgramD3D::setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<4, 4>(location, count, transpose, value, GL_FLOAT_MAT4); } -void ProgramD3D::setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix2x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<2, 3>(location, count, transpose, value, GL_FLOAT_MAT2x3); } -void ProgramD3D::setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix3x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<3, 2>(location, count, transpose, value, GL_FLOAT_MAT3x2); } -void ProgramD3D::setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix2x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<2, 4>(location, count, transpose, value, GL_FLOAT_MAT2x4); } -void ProgramD3D::setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix4x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<4, 2>(location, count, transpose, value, GL_FLOAT_MAT4x2); } -void ProgramD3D::setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix3x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<3, 4>(location, count, transpose, value, GL_FLOAT_MAT3x4); } -void ProgramD3D::setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +void ProgramD3D::setUniformMatrix4x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) { setUniformMatrixfv<4, 3>(location, count, transpose, value, GL_FLOAT_MAT4x3); } @@ -1370,7 +1767,7 @@ void ProgramD3D::setUniformBlockBinding(GLuint /*uniformBlockIndex*/, void ProgramD3D::defineUniformsAndAssignRegisters() { D3DUniformMap uniformMap; - const gl::Shader *vertexShader = mData.getAttachedVertexShader(); + const gl::Shader *vertexShader = mData.getAttachedVertexShader(); for (const sh::Uniform &vertexUniform : vertexShader->getUniforms()) { @@ -1380,7 +1777,7 @@ void ProgramD3D::defineUniformsAndAssignRegisters() } } - const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); + const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); for (const sh::Uniform &fragmentUniform : fragmentShader->getUniforms()) { if (fragmentUniform.staticUse) @@ -1454,7 +1851,7 @@ void ProgramD3D::defineUniform(GLenum shaderType, for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++) { - const sh::ShaderVariable &field = uniform.fields[fieldIndex]; + const sh::ShaderVariable &field = uniform.fields[fieldIndex]; const std::string &fieldFullName = (fullName + elementString + "." + field.name); defineUniform(shaderType, field, fieldFullName, encoder, uniformMap); @@ -1515,19 +1912,19 @@ void ProgramD3D::defineUniform(GLenum shaderType, } template -static inline void SetIfDirty(T *dest, const T& source, bool *dirtyFlag) +static inline void SetIfDirty(T *dest, const T &source, bool *dirtyFlag) { ASSERT(dest != NULL); ASSERT(dirtyFlag != NULL); *dirtyFlag = *dirtyFlag || (memcmp(dest, &source, sizeof(T)) != 0); - *dest = source; + *dest = source; } template void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum targetUniformType) { - const int components = gl::VariableComponentCount(targetUniformType); + const int components = gl::VariableComponentCount(targetUniformType); const GLenum targetBoolType = gl::VariableBoolVectorType(targetUniformType); D3DUniform *targetUniform = getD3DUniformFromLocation(location); @@ -1542,7 +1939,7 @@ void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum for (unsigned int i = 0; i < count; i++) { - T *dest = target + (i * 4); + T *dest = target + (i * 4); const T *source = v + (i * components); for (int c = 0; c < components; c++) @@ -1561,12 +1958,13 @@ void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum for (unsigned int i = 0; i < count; i++) { - GLint *dest = boolParams + (i * 4); + GLint *dest = boolParams + (i * 4); const T *source = v + (i * components); for (int c = 0; c < components; c++) { - SetIfDirty(dest + c, (source[c] == static_cast(0)) ? GL_FALSE : GL_TRUE, &targetUniform->dirty); + SetIfDirty(dest + c, (source[c] == static_cast(0)) ? GL_FALSE : GL_TRUE, + &targetUniform->dirty); } for (int c = components; c < 4; c++) { @@ -1584,8 +1982,8 @@ void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum for (unsigned int i = 0; i < count; i++) { - GLint *dest = target + (i * 4); - const GLint *source = reinterpret_cast(v) + (i * components); + GLint *dest = target + (i * 4); + const GLint *source = reinterpret_cast(v) + (i * components); SetIfDirty(dest + 0, source[0], &targetUniform->dirty); SetIfDirty(dest + 1, 0, &targetUniform->dirty); @@ -1598,75 +1996,8 @@ void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum mDirtySamplerMapping = true; } } - else UNREACHABLE(); -} - -template -bool transposeMatrix(T *target, const GLfloat *value, int targetWidth, int targetHeight, int srcWidth, int srcHeight) -{ - bool dirty = false; - int copyWidth = std::min(targetHeight, srcWidth); - int copyHeight = std::min(targetWidth, srcHeight); - - for (int x = 0; x < copyWidth; x++) - { - for (int y = 0; y < copyHeight; y++) - { - SetIfDirty(target + (x * targetWidth + y), static_cast(value[y * srcWidth + x]), &dirty); - } - } - // clear unfilled right side - for (int y = 0; y < copyWidth; y++) - { - for (int x = copyHeight; x < targetWidth; x++) - { - SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); - } - } - // clear unfilled bottom. - for (int y = copyWidth; y < targetHeight; y++) - { - for (int x = 0; x < targetWidth; x++) - { - SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); - } - } - - return dirty; -} - -template -bool expandMatrix(T *target, const GLfloat *value, int targetWidth, int targetHeight, int srcWidth, int srcHeight) -{ - bool dirty = false; - int copyWidth = std::min(targetWidth, srcWidth); - int copyHeight = std::min(targetHeight, srcHeight); - - for (int y = 0; y < copyHeight; y++) - { - for (int x = 0; x < copyWidth; x++) - { - SetIfDirty(target + (y * targetWidth + x), static_cast(value[y * srcWidth + x]), &dirty); - } - } - // clear unfilled right side - for (int y = 0; y < copyHeight; y++) - { - for (int x = copyWidth; x < targetWidth; x++) - { - SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); - } - } - // clear unfilled bottom. - for (int y = copyHeight; y < targetHeight; y++) - { - for (int x = 0; x < targetWidth; x++) - { - SetIfDirty(target + (y * targetWidth + x), static_cast(0), &dirty); - } - } - - return dirty; + else + UNREACHABLE(); } template @@ -1691,11 +2022,13 @@ void ProgramD3D::setUniformMatrixfv(GLint location, // Internally store matrices as transposed versions to accomodate HLSL matrix indexing if (transpose == GL_FALSE) { - targetUniform->dirty = transposeMatrix(target, value, 4, rows, rows, cols) || targetUniform->dirty; + targetUniform->dirty = TransposeMatrix(target, value, 4, rows, rows, cols) || + targetUniform->dirty; } else { - targetUniform->dirty = expandMatrix(target, value, 4, rows, cols, rows) || targetUniform->dirty; + targetUniform->dirty = + ExpandMatrix(target, value, 4, rows, cols, rows) || targetUniform->dirty; } target += targetMatrixStride; value += cols * rows; @@ -1781,7 +2114,11 @@ void ProgramD3D::reset() { SafeDeleteContainer(mVertexExecutables); SafeDeleteContainer(mPixelExecutables); - SafeDelete(mGeometryExecutable); + + for (auto &element : mGeometryExecutables) + { + SafeDelete(element); + } mVertexHLSL.clear(); mVertexWorkarounds = D3DCompilerWorkarounds(); @@ -1791,6 +2128,7 @@ void ProgramD3D::reset() mUsesFragDepth = false; mPixelShaderKey.clear(); mUsesPointSize = false; + mUsesFlatInterpolation = false; SafeDeleteContainer(mD3DUniforms); mD3DUniformBlocks.clear(); @@ -1802,13 +2140,15 @@ void ProgramD3D::reset() mSamplersVS.clear(); mUsedVertexSamplerRange = 0; - mUsedPixelSamplerRange = 0; - mDirtySamplerMapping = true; + mUsedPixelSamplerRange = 0; + mDirtySamplerMapping = true; std::fill(mSemanticIndexes, mSemanticIndexes + ArraySize(mSemanticIndexes), -1); std::fill(mAttributesByLayout, mAttributesByLayout + ArraySize(mAttributesByLayout), -1); - mTransformFeedbackLinkedVaryings.clear(); + mStreamOutVaryings.clear(); + + mGeometryShaderPreamble.clear(); } unsigned int ProgramD3D::getSerial() const @@ -1853,15 +2193,16 @@ void ProgramD3D::initAttributesByLayout() AttributeSorter(mSemanticIndexes)); } -void ProgramD3D::sortAttributesByLayout(const std::vector &unsortedAttributes, - int sortedSemanticIndicesOut[gl::MAX_VERTEX_ATTRIBS], - const rx::TranslatedAttribute *sortedAttributesOut[gl::MAX_VERTEX_ATTRIBS]) const +void ProgramD3D::sortAttributesByLayout( + const std::vector &unsortedAttributes, + int sortedSemanticIndicesOut[gl::MAX_VERTEX_ATTRIBS], + const rx::TranslatedAttribute *sortedAttributesOut[gl::MAX_VERTEX_ATTRIBS]) const { for (size_t attribIndex = 0; attribIndex < unsortedAttributes.size(); ++attribIndex) { - int oldIndex = mAttributesByLayout[attribIndex]; + int oldIndex = mAttributesByLayout[attribIndex]; sortedSemanticIndicesOut[attribIndex] = mSemanticIndexes[oldIndex]; - sortedAttributesOut[attribIndex] = &unsortedAttributes[oldIndex]; + sortedAttributesOut[attribIndex] = &unsortedAttributes[oldIndex]; } } @@ -1887,19 +2228,66 @@ void ProgramD3D::updateCachedInputLayout(const gl::State &state) } } -void ProgramD3D::gatherTransformFeedbackVaryings( - const std::vector &linkedVaryings) +void ProgramD3D::gatherTransformFeedbackVaryings(const VaryingPacking &varyingPacking) { + const auto &builtins = varyingPacking.builtins(SHADER_VERTEX); + + const std::string &varyingSemantic = + GetVaryingSemantic(mRenderer->getMajorShaderModel(), usesPointSize()); + // Gather the linked varyings that are used for transform feedback, they should all exist. - mTransformFeedbackLinkedVaryings.clear(); - for (const std::string &tfVaryingName : mData.getTransformFeedbackVaryingNames()) + mStreamOutVaryings.clear(); + + const auto &tfVaryingNames = mData.getTransformFeedbackVaryingNames(); + for (unsigned int outputSlot = 0; outputSlot < static_cast(tfVaryingNames.size()); + ++outputSlot) { - for (const gl::LinkedVarying &linkedVarying : linkedVaryings) + const auto &tfVaryingName = tfVaryingNames[outputSlot]; + if (tfVaryingName == "gl_Position") { - if (tfVaryingName == linkedVarying.name) + if (builtins.glPosition.enabled) { - mTransformFeedbackLinkedVaryings.push_back(linkedVarying); - break; + mStreamOutVaryings.push_back(D3DVarying(builtins.glPosition.semantic, + builtins.glPosition.index, 4, outputSlot)); + } + } + else if (tfVaryingName == "gl_FragCoord") + { + if (builtins.glFragCoord.enabled) + { + mStreamOutVaryings.push_back(D3DVarying(builtins.glFragCoord.semantic, + builtins.glFragCoord.index, 4, outputSlot)); + } + } + else if (tfVaryingName == "gl_PointSize") + { + if (builtins.glPointSize.enabled) + { + mStreamOutVaryings.push_back(D3DVarying("PSIZE", 0, 1, outputSlot)); + } + } + else + { + for (const PackedVaryingRegister ®isterInfo : varyingPacking.getRegisterList()) + { + const auto &varying = *registerInfo.packedVarying->varying; + GLenum transposedType = gl::TransposeMatrixType(varying.type); + int componentCount = gl::VariableColumnCount(transposedType); + ASSERT(!varying.isBuiltIn()); + + // Transform feedback for varying structs is underspecified. + // See Khronos bug 9856. + // TODO(jmadill): Figure out how to be spec-compliant here. + if (registerInfo.packedVarying->isStructField() || varying.isStruct()) + continue; + + // There can be more than one register assigned to a particular varying, and each + // register needs its own stream out entry. + if (tfVaryingName == varying.name) + { + mStreamOutVaryings.push_back(D3DVarying( + varyingSemantic, registerInfo.semanticIndex, componentCount, outputSlot)); + } } } } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h index f78364884c5c..b5eea9b37338 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h @@ -80,6 +80,54 @@ struct D3DUniformBlock unsigned int psRegisterIndex; }; +struct D3DVarying final +{ + D3DVarying(); + D3DVarying(const std::string &semanticNameIn, + unsigned int semanticIndexIn, + unsigned int componentCountIn, + unsigned int outputSlotIn); + + D3DVarying(const D3DVarying &) = default; + D3DVarying &operator=(const D3DVarying &) = default; + + std::string semanticName; + unsigned int semanticIndex; + unsigned int componentCount; + unsigned int outputSlot; +}; + +class ProgramD3DMetadata : angle::NonCopyable +{ + public: + ProgramD3DMetadata(int rendererMajorShaderModel, + const std::string &shaderModelSuffix, + bool usesInstancedPointSpriteEmulation, + const ShaderD3D *vertexShader, + const ShaderD3D *fragmentShader); + + int getRendererMajorShaderModel() const; + bool usesBroadcast(const gl::Data &data) const; + bool usesFragDepth(const gl::Program::Data &programData) const; + bool usesPointCoord() const; + bool usesFragCoord() const; + bool usesPointSize() const; + bool usesInsertedPointCoordValue() const; + bool addsPointCoordToVertexShader() const; + bool usesTransformFeedbackGLPosition() const; + bool usesSystemValuePointSize() const; + bool usesMultipleFragmentOuts() const; + GLint getMajorShaderVersion() const; + const ShaderD3D *getFragmentShader() const; + + private: + const int mRendererMajorShaderModel; + const std::string mShaderModelSuffix; + const bool mUsesInstancedPointSpriteEmulation; + const ShaderD3D *mVertexShader; + const ShaderD3D *mFragmentShader; +}; + class ProgramD3D : public ProgramImpl { public: @@ -90,23 +138,33 @@ class ProgramD3D : public ProgramImpl const std::vector &getPixelShaderKey() { return mPixelShaderKey; } - GLint getSamplerMapping(gl::SamplerType type, unsigned int samplerIndex, const gl::Caps &caps) const; + GLint getSamplerMapping(gl::SamplerType type, + unsigned int samplerIndex, + const gl::Caps &caps) const; GLenum getSamplerTextureType(gl::SamplerType type, unsigned int samplerIndex) const; GLint getUsedSamplerRange(gl::SamplerType type) const; void updateSamplerMapping(); bool usesPointSize() const { return mUsesPointSize; } bool usesPointSpriteEmulation() const; - bool usesGeometryShader() const; + bool usesGeometryShader(GLenum drawMode) const; bool usesInstancedPointSpriteEmulation() const; LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream); gl::Error save(gl::BinaryOutputStream *stream); - gl::Error getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, ShaderExecutableD3D **outExectuable); - gl::Error getPixelExecutableForOutputLayout(const std::vector &outputLayout, ShaderExecutableD3D **outExectuable, gl::InfoLog *infoLog); - gl::Error getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout, ShaderExecutableD3D **outExectuable, gl::InfoLog *infoLog); - ShaderExecutableD3D *getGeometryExecutable() const { return mGeometryExecutable; } + gl::Error getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo, + ShaderExecutableD3D **outExectuable); + gl::Error getPixelExecutableForOutputLayout(const std::vector &outputLayout, + ShaderExecutableD3D **outExectuable, + gl::InfoLog *infoLog); + gl::Error getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout, + ShaderExecutableD3D **outExectuable, + gl::InfoLog *infoLog); + gl::Error getGeometryExecutableForPrimitiveType(const gl::Data &data, + GLenum drawMode, + ShaderExecutableD3D **outExecutable, + gl::InfoLog *infoLog); LinkResult link(const gl::Data &data, gl::InfoLog &infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; @@ -116,7 +174,7 @@ class ProgramD3D : public ProgramImpl sh::BlockMemberInfo *memberInfoOut) const override; void initializeUniformStorage(); - gl::Error applyUniforms(); + gl::Error applyUniforms(GLenum drawMode); gl::Error applyUniformBuffers(const gl::Data &data); void dirtyAllUniforms(); @@ -132,15 +190,42 @@ class ProgramD3D : public ProgramImpl void setUniform2uiv(GLint location, GLsizei count, const GLuint *v); void setUniform3uiv(GLint location, GLsizei count, const GLuint *v); void setUniform4uiv(GLint location, GLsizei count, const GLuint *v); - void setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); - void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); + void setUniformMatrix2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix2x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix3x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix2x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix4x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix3x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); + void setUniformMatrix4x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value); void setUniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) override; @@ -149,9 +234,10 @@ class ProgramD3D : public ProgramImpl unsigned int getSerial() const; - void sortAttributesByLayout(const std::vector &unsortedAttributes, - int sortedSemanticIndicesOut[gl::MAX_VERTEX_ATTRIBS], - const rx::TranslatedAttribute *sortedAttributesOut[gl::MAX_VERTEX_ATTRIBS]) const; + void sortAttributesByLayout( + const std::vector &unsortedAttributes, + int sortedSemanticIndicesOut[gl::MAX_VERTEX_ATTRIBS], + const rx::TranslatedAttribute *sortedAttributesOut[gl::MAX_VERTEX_ATTRIBS]) const; const SemanticIndexArray &getSemanticIndexes() const { return mSemanticIndexes; } const SemanticIndexArray &getAttributesByLayout() const { return mAttributesByLayout; } @@ -187,10 +273,14 @@ class ProgramD3D : public ProgramImpl class PixelExecutable { public: - PixelExecutable(const std::vector &outputSignature, ShaderExecutableD3D *shaderExecutable); + PixelExecutable(const std::vector &outputSignature, + ShaderExecutableD3D *shaderExecutable); ~PixelExecutable(); - bool matchesSignature(const std::vector &signature) const { return mOutputSignature == signature; } + bool matchesSignature(const std::vector &signature) const + { + return mOutputSignature == signature; + } const std::vector &outputSignature() const { return mOutputSignature; } ShaderExecutableD3D *shaderExecutable() const { return mShaderExecutable; } @@ -230,16 +320,18 @@ class ProgramD3D : public ProgramImpl GLuint *outUsedRange); template - void setUniform(GLint location, GLsizei count, const T* v, GLenum targetUniformType); + void setUniform(GLint location, GLsizei count, const T *v, GLenum targetUniformType); template - void setUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value, GLenum targetUniformType); + void setUniformMatrixfv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value, + GLenum targetUniformType); - LinkResult compileProgramExecutables(gl::InfoLog &infoLog, - int registers, - const std::vector &packedVaryings); + LinkResult compileProgramExecutables(const gl::Data &data, gl::InfoLog &infoLog); - void gatherTransformFeedbackVaryings(const std::vector &varyings); + void gatherTransformFeedbackVaryings(const VaryingPacking &varyings); D3DUniform *getD3DUniformByName(const std::string &name); D3DUniform *getD3DUniformFromLocation(GLint location); @@ -257,7 +349,7 @@ class ProgramD3D : public ProgramImpl std::vector mVertexExecutables; std::vector mPixelExecutables; - ShaderExecutableD3D *mGeometryExecutable; + std::vector mGeometryExecutables; std::string mVertexHLSL; D3DCompilerWorkarounds mVertexWorkarounds; @@ -267,7 +359,13 @@ class ProgramD3D : public ProgramImpl bool mUsesFragDepth; std::vector mPixelShaderKey; + // Common code for all dynamic geometry shaders. Consists mainly of the GS input and output + // structures, built from the linked varying info. We store the string itself instead of the + // packed varyings for simplicity. + std::string mGeometryShaderPreamble; + bool mUsesPointSize; + bool mUsesFlatInterpolation; UniformStorageD3D *mVertexUniformStorage; UniformStorageD3D *mFragmentUniformStorage; @@ -291,7 +389,7 @@ class ProgramD3D : public ProgramImpl VertexExecutable::Signature mCachedVertexSignature; gl::InputLayout mCachedInputLayout; - std::vector mTransformFeedbackLinkedVaryings; + std::vector mStreamOutVaryings; std::vector mD3DUniforms; std::vector mD3DUniformBlocks; @@ -301,7 +399,6 @@ class ProgramD3D : public ProgramImpl static unsigned int issueSerial(); static unsigned int mCurrentSerial; }; - } -#endif // LIBANGLE_RENDERER_D3D_PROGRAMD3D_H_ +#endif // LIBANGLE_RENDERER_D3D_PROGRAMD3D_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp index 26740aadc995..849ee8ba89f4 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp @@ -8,22 +8,23 @@ #include "libANGLE/renderer/d3d/RendererD3D.h" -#include "common/MemoryBuffer.h" #include "common/debug.h" +#include "common/MemoryBuffer.h" #include "common/utilities.h" #include "libANGLE/Display.h" +#include "libANGLE/formatutils.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" -#include "libANGLE/ResourceManager.h" -#include "libANGLE/State.h" -#include "libANGLE/VertexArray.h" -#include "libANGLE/formatutils.h" #include "libANGLE/renderer/d3d/BufferD3D.h" #include "libANGLE/renderer/d3d/CompilerD3D.h" +#include "libANGLE/renderer/d3d/DeviceD3D.h" #include "libANGLE/renderer/d3d/DisplayD3D.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" #include "libANGLE/renderer/d3d/ProgramD3D.h" #include "libANGLE/renderer/d3d/SamplerD3D.h" +#include "libANGLE/ResourceManager.h" +#include "libANGLE/State.h" +#include "libANGLE/VertexArray.h" namespace rx { @@ -44,7 +45,8 @@ RendererD3D::RendererD3D(egl::Display *display) mDeviceLost(false), mAnnotator(nullptr), mScratchMemoryBufferResetCounter(0), - mWorkaroundsInitialized(false) + mWorkaroundsInitialized(false), + mEGLDevice(nullptr) { } @@ -55,6 +57,8 @@ RendererD3D::~RendererD3D() void RendererD3D::cleanup() { + SafeDelete(mEGLDevice); + mScratchMemoryBuffer.resize(0); for (auto &incompleteTexture : mIncompleteTextures) { @@ -134,12 +138,6 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, GLsizei instances, const gl::IndexRange &indexRange) { - if (data.state->isPrimitiveRestartEnabled()) - { - UNIMPLEMENTED(); - return gl::Error(GL_INVALID_OPERATION, "Primitive restart not implemented"); - } - gl::Program *program = data.state->getProgram(); ASSERT(program != nullptr); ProgramD3D *programD3D = GetImplAs(program); @@ -170,13 +168,12 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, return error; } - gl::VertexArray *vao = data.state->getVertexArray(); TranslatedIndexData indexInfo; indexInfo.indexRange = indexRange; SourceIndexData sourceIndexInfo; - error = applyIndexBuffer(indices, vao->getElementArrayBuffer().get(), count, mode, type, &indexInfo, &sourceIndexInfo); + error = applyIndexBuffer(data, indices, count, mode, type, &indexInfo, &sourceIndexInfo); if (error.isError()) { return error; @@ -195,7 +192,7 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, return error; } - error = applyShaders(data); + error = applyShaders(data, mode); if (error.isError()) { return error; @@ -215,8 +212,7 @@ gl::Error RendererD3D::genericDrawElements(const gl::Data &data, if (!skipDraw(data, mode)) { - error = drawElementsImpl(mode, count, type, indices, vao->getElementArrayBuffer().get(), - indexInfo, instances, usesPointSize); + error = drawElementsImpl(data, indexInfo, mode, count, type, indices, instances); if (error.isError()) { return error; @@ -270,7 +266,7 @@ gl::Error RendererD3D::genericDrawArrays(const gl::Data &data, return error; } - error = applyShaders(data); + error = applyShaders(data, mode); if (error.isError()) { return error; @@ -290,7 +286,7 @@ gl::Error RendererD3D::genericDrawArrays(const gl::Data &data, if (!skipDraw(data, mode)) { - error = drawArraysImpl(data, mode, count, instances, usesPointSize); + error = drawArraysImpl(data, mode, count, instances); if (error.isError()) { return error; @@ -419,7 +415,8 @@ gl::Error RendererD3D::applyState(const gl::Data &data, GLenum drawMode) { mask = 0xFFFFFFFF; } - error = setBlendState(framebufferObject, data.state->getBlendState(), data.state->getBlendColor(), mask); + error = setBlendState(framebufferObject, data.state->getBlendState(), + data.state->getBlendColor(), mask); if (error.isError()) { return error; @@ -436,21 +433,19 @@ gl::Error RendererD3D::applyState(const gl::Data &data, GLenum drawMode) } // Applies the shaders and shader constants to the Direct3D device -gl::Error RendererD3D::applyShaders(const gl::Data &data) +gl::Error RendererD3D::applyShaders(const gl::Data &data, GLenum drawMode) { gl::Program *program = data.state->getProgram(); ProgramD3D *programD3D = GetImplAs(program); programD3D->updateCachedInputLayout(*data.state); - const gl::Framebuffer *fbo = data.state->getDrawFramebuffer(); - - gl::Error error = applyShaders(program, fbo, data.state->getRasterizerState().rasterizerDiscard, data.state->isTransformFeedbackActiveUnpaused()); + gl::Error error = applyShadersImpl(data, drawMode); if (error.isError()) { return error; } - return programD3D->applyUniforms(); + return programD3D->applyUniforms(drawMode); } // For each Direct3D sampler of either the pixel or vertex stage, @@ -743,4 +738,16 @@ gl::DebugAnnotator *RendererD3D::getAnnotator() return mAnnotator; } +egl::Error RendererD3D::getEGLDevice(DeviceImpl **device) +{ + egl::Error error = initializeEGLDevice(&mEGLDevice); + if (error.isError()) + { + return error; + } + + *device = static_cast(mEGLDevice); + + return egl::Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h index b6c29b934b94..37cc310723a3 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h @@ -12,7 +12,8 @@ #include "common/debug.h" #include "common/MemoryBuffer.h" #include "libANGLE/Data.h" -#include "libANGLe/formatutils.h" +#include "libANGLE/Device.h" +#include "libANGLE/formatutils.h" #include "libANGLE/renderer/Renderer.h" #include "libANGLE/renderer/d3d/VertexDataManager.h" #include "libANGLE/renderer/d3d/formatutilsD3D.h" @@ -29,15 +30,17 @@ class ConfigSet; namespace gl { -class InfoLog; -struct LinkedVarying; -class Texture; class DebugAnnotator; +class InfoLog; +class Texture; +struct LinkedVarying; } namespace rx { struct D3DUniform; +struct D3DVarying; +class DeviceD3D; class EGLImageD3D; class ImageD3D; class IndexBuffer; @@ -53,7 +56,8 @@ enum ShaderType { SHADER_VERTEX, SHADER_PIXEL, - SHADER_GEOMETRY + SHADER_GEOMETRY, + SHADER_TYPE_MAX }; struct DeviceIdentifier @@ -150,8 +154,11 @@ class RendererD3D : public Renderer, public BufferFactoryD3D const std::vector &fragmentUniformBuffers) = 0; virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState) = 0; - virtual gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, + virtual gl::Error setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, unsigned int sampleMask) = 0; + virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, int stencilBackRef, bool frontFaceCCW) = 0; @@ -160,15 +167,18 @@ class RendererD3D : public Renderer, public BufferFactoryD3D bool ignoreViewport) = 0; virtual gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) = 0; - virtual gl::Error applyShaders(gl::Program *program, - const gl::Framebuffer *framebuffer, - bool rasterizerDiscard, - bool transformFeedbackActive) = 0; virtual gl::Error applyUniforms(const ProgramD3D &programD3D, + GLenum drawMode, const std::vector &uniformArray) = 0; virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize) = 0; virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo) = 0; - virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo) = 0; + virtual gl::Error applyIndexBuffer(const gl::Data &data, + const GLvoid *indices, + GLsizei count, + GLenum mode, + GLenum type, + TranslatedIndexData *indexInfo, + SourceIndexData *sourceIndexInfo) = 0; virtual void applyTransformFeedbackBuffers(const gl::State& state) = 0; virtual void markAllStateDirty() = 0; @@ -197,12 +207,18 @@ class RendererD3D : public Renderer, public BufferFactoryD3D virtual gl::Error createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT) = 0; // Shader operations - virtual gl::Error loadExecutable(const void *function, size_t length, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, ShaderExecutableD3D **outExecutable) = 0; - virtual gl::Error compileToExecutable(gl::InfoLog &infoLog, const std::string &shaderHLSL, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, const D3DCompilerWorkarounds &workarounds, + virtual gl::Error loadExecutable(const void *function, + size_t length, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) = 0; + virtual gl::Error compileToExecutable(gl::InfoLog &infoLog, + const std::string &shaderHLSL, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + const D3DCompilerWorkarounds &workarounds, ShaderExecutableD3D **outExectuable) = 0; virtual UniformStorageD3D *createUniformStorage(size_t storageSize) = 0; @@ -223,11 +239,6 @@ class RendererD3D : public Renderer, public BufferFactoryD3D virtual gl::Error fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget, GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea) = 0; - void syncState(const gl::State & /*state*/, const gl::State::DirtyBits &bitmask) override - { - // TODO(jmadill): implement state sync for D3D renderers; - } - // Device lost void notifyDeviceLost() override; virtual bool resetDevice() = 0; @@ -244,13 +255,18 @@ class RendererD3D : public Renderer, public BufferFactoryD3D // In D3D11, faster than calling setTexture a jillion times virtual gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) = 0; + egl::Error getEGLDevice(DeviceImpl **device); + protected: virtual bool getLUID(LUID *adapterLuid) const = 0; + virtual gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) = 0; void cleanup(); virtual void createAnnotator() = 0; + virtual egl::Error initializeEGLDevice(DeviceD3D **outDevice) = 0; + // dirtyPointer is a special value that will make the comparison with any valid pointer fail and force the renderer to re-apply the state. static const uintptr_t DirtyPointer; @@ -280,16 +296,14 @@ class RendererD3D : public Renderer, public BufferFactoryD3D virtual gl::Error drawArraysImpl(const gl::Data &data, GLenum mode, GLsizei count, - GLsizei instances, - bool usesPointSize) = 0; - virtual gl::Error drawElementsImpl(GLenum mode, + GLsizei instances) = 0; + virtual gl::Error drawElementsImpl(const gl::Data &data, + const TranslatedIndexData &indexInfo, + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, - gl::Buffer *elementArrayBuffer, - const TranslatedIndexData &indexInfo, - GLsizei instances, - bool usesPointSize) = 0; + GLsizei instances) = 0; //FIXME(jmadill): std::array is currently prohibited by Chromium style guide typedef std::array FramebufferTextureArray; @@ -299,7 +313,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D gl::Error applyRenderTarget(const gl::Data &data, GLenum drawMode, bool ignoreViewport); gl::Error applyState(const gl::Data &data, GLenum drawMode); - gl::Error applyShaders(const gl::Data &data); + gl::Error applyShaders(const gl::Data &data, GLenum drawMode); gl::Error applyTextures(const gl::Data &data, gl::SamplerType shaderType, const FramebufferTextureArray &framebufferTextures, size_t framebufferTextureCount); gl::Error applyTextures(const gl::Data &data); @@ -320,6 +334,8 @@ class RendererD3D : public Renderer, public BufferFactoryD3D mutable bool mWorkaroundsInitialized; mutable WorkaroundsD3D mWorkarounds; + + DeviceD3D *mEGLDevice; }; struct dx_VertexConstants diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp index ade6f9eebff1..1ecbfb741042 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp @@ -117,7 +117,8 @@ ShShaderOutput ShaderD3D::getCompilerOutputType() const return mCompilerOutputType; } -int ShaderD3D::prepareSourceAndReturnOptions(std::stringstream *shaderSourceStream) +int ShaderD3D::prepareSourceAndReturnOptions(std::stringstream *shaderSourceStream, + std::string *sourcePath) { uncompile(); @@ -128,10 +129,9 @@ int ShaderD3D::prepareSourceAndReturnOptions(std::stringstream *shaderSourceStre #if !defined(ANGLE_ENABLE_WINDOWS_STORE) if (gl::DebugAnnotationsActive()) { - std::string sourcePath = getTempPath(); - writeFile(sourcePath.c_str(), source.c_str(), source.length()); + *sourcePath = getTempPath(); + writeFile(sourcePath->c_str(), source.c_str(), source.length()); additionalOptions |= SH_LINE_DIRECTIVES | SH_SOURCE_PATH; - *shaderSourceStream << sourcePath; } #endif @@ -192,15 +192,11 @@ bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLo } } -#if ANGLE_SHADER_DEBUG_INFO == ANGLE_ENABLED mDebugInfo += - std::string("// ") + GetShaderTypeString(mData->getShaderType()) + " SHADER BEGIN\n"; - mDebugInfo += "\n// GLSL BEGIN\n\n" + source + "\n\n// GLSL END\n\n\n"; + std::string("// ") + GetShaderTypeString(mData.getShaderType()) + " SHADER BEGIN\n"; + mDebugInfo += "\n// GLSL BEGIN\n\n" + mData.getSource() + "\n\n// GLSL END\n\n\n"; mDebugInfo += "// INITIAL HLSL BEGIN\n\n" + translatedSource + "\n// INITIAL HLSL END\n\n\n"; // Successive steps will append more info -#else - mDebugInfo += translatedSource; -#endif return true; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h b/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h index f61917a7efc0..47a73dc27b4b 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h @@ -21,14 +21,13 @@ struct D3DCompilerWorkarounds; class ShaderD3D : public ShaderImpl { - friend class DynamicHLSL; - public: ShaderD3D(const gl::Shader::Data &data); virtual ~ShaderD3D(); // ShaderImpl implementation - int prepareSourceAndReturnOptions(std::stringstream *sourceStream) override; + int prepareSourceAndReturnOptions(std::stringstream *sourceStream, + std::string *sourcePath) override; bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override; std::string getDebugInfo() const override; @@ -36,13 +35,20 @@ class ShaderD3D : public ShaderImpl void uncompile(); unsigned int getUniformRegister(const std::string &uniformName) const; unsigned int getInterfaceBlockRegister(const std::string &blockName) const; - void appendDebugInfo(const std::string &info) { mDebugInfo += info; } + void appendDebugInfo(const std::string &info) const { mDebugInfo += info; } void generateWorkarounds(D3DCompilerWorkarounds *workarounds) const; - bool usesDepthRange() const { return mUsesDepthRange; } - bool usesPointSize() const { return mUsesPointSize; } - bool usesDeferredInit() const { return mUsesDeferredInit; } + + bool usesMultipleRenderTargets() const { return mUsesMultipleRenderTargets; } + bool usesFragColor() const { return mUsesFragColor; } + bool usesFragData() const { return mUsesFragData; } + bool usesFragCoord() const { return mUsesFragCoord; } bool usesFrontFacing() const { return mUsesFrontFacing; } + bool usesPointSize() const { return mUsesPointSize; } + bool usesPointCoord() const { return mUsesPointCoord; } + bool usesDepthRange() const { return mUsesDepthRange; } + bool usesFragDepth() const { return mUsesFragDepth; } + bool usesDeferredInit() const { return mUsesDeferredInit; } ShShaderOutput getCompilerOutputType() const; @@ -62,11 +68,10 @@ class ShaderD3D : public ShaderImpl bool mRequiresIEEEStrictCompiling; ShShaderOutput mCompilerOutputType; - std::string mDebugInfo; + mutable std::string mDebugInfo; std::map mUniformRegisterMap; std::map mInterfaceBlockRegisterMap; }; - } -#endif // LIBANGLE_RENDERER_D3D_SHADERD3D_H_ +#endif // LIBANGLE_RENDERER_D3D_SHADERD3D_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp b/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp index ee367c60d67e..cdb2c4747153 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp @@ -141,9 +141,6 @@ GLenum TextureD3D::getBaseLevelInternalFormat() const bool TextureD3D::shouldUseSetData(const ImageD3D *image) const { - if (image->isDirty()) - return false; - if (!mRenderer->getWorkarounds().setDataFasterThanImageUpload) { return false; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.cpp b/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.cpp new file mode 100644 index 000000000000..f2654d34e33a --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.cpp @@ -0,0 +1,397 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// VaryingPacking: +// Class which describes a mapping from varyings to registers in D3D +// for linking between shader stages. +// + +#include "libANGLE/renderer/d3d/VaryingPacking.h" + +#include "common/utilities.h" +#include "compiler/translator/blocklayoutHLSL.h" +#include "libANGLE/renderer/d3d/DynamicHLSL.h" +#include "libANGLE/renderer/d3d/ProgramD3D.h" + +namespace rx +{ + +// Implementation of VaryingPacking::BuiltinVarying +VaryingPacking::BuiltinVarying::BuiltinVarying() : enabled(false), index(0), systemValue(false) +{ +} + +std::string VaryingPacking::BuiltinVarying::str() const +{ + return (systemValue ? semantic : (semantic + Str(index))); +} + +void VaryingPacking::BuiltinVarying::enableSystem(const std::string &systemValueSemantic) +{ + enabled = true; + semantic = systemValueSemantic; + systemValue = true; +} + +void VaryingPacking::BuiltinVarying::enable(const std::string &semanticVal, unsigned int indexVal) +{ + enabled = true; + semantic = semanticVal; + index = indexVal; +} + +// Implementation of VaryingPacking +VaryingPacking::VaryingPacking(GLuint maxVaryingVectors) + : mRegisterMap(maxVaryingVectors), mBuiltinInfo(SHADER_TYPE_MAX) +{ +} + +// Packs varyings into generic varying registers, using the algorithm from +// See [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111 +// Also [OpenGL ES Shading Language 3.00 rev. 4] Section 11 page 119 +// Returns false if unsuccessful. +bool VaryingPacking::packVarying(const PackedVarying &packedVarying) +{ + unsigned int varyingRows = 0; + unsigned int varyingColumns = 0; + + const auto &varying = *packedVarying.varying; + + // "Non - square matrices of type matCxR consume the same space as a square matrix of type matN + // where N is the greater of C and R.Variables of type mat2 occupies 2 complete rows." + // Here we are a bit more conservative and allow packing non-square matrices more tightly. + // Make sure we use transposed matrix types to count registers correctly. + ASSERT(!varying.isStruct()); + GLenum transposedType = gl::TransposeMatrixType(varying.type); + varyingRows = gl::VariableRowCount(transposedType); + varyingColumns = gl::VariableColumnCount(transposedType); + + // "Arrays of size N are assumed to take N times the size of the base type" + varyingRows *= varying.elementCount(); + + unsigned int maxVaryingVectors = static_cast(mRegisterMap.size()); + + // "For 2, 3 and 4 component variables packing is started using the 1st column of the 1st row. + // Variables are then allocated to successive rows, aligning them to the 1st column." + if (varyingColumns >= 2 && varyingColumns <= 4) + { + for (unsigned int row = 0; row <= maxVaryingVectors - varyingRows; ++row) + { + if (isFree(row, 0, varyingRows, varyingColumns)) + { + insert(row, 0, packedVarying); + return true; + } + } + + // "For 2 component variables, when there are no spare rows, the strategy is switched to + // using the highest numbered row and the lowest numbered column where the variable will + // fit." + if (varyingColumns == 2) + { + for (unsigned int r = maxVaryingVectors - varyingRows + 1; r-- >= 1;) + { + if (isFree(r, 2, varyingRows, 2)) + { + insert(r, 2, packedVarying); + return true; + } + } + } + + return false; + } + + // "1 component variables have their own packing rule. They are packed in order of size, largest + // first. Each variable is placed in the column that leaves the least amount of space in the + // column and aligned to the lowest available rows within that column." + ASSERT(varyingColumns == 1); + unsigned int contiguousSpace[4] = {0}; + unsigned int bestContiguousSpace[4] = {0}; + unsigned int totalSpace[4] = {0}; + + for (unsigned int row = 0; row < maxVaryingVectors; ++row) + { + for (unsigned int column = 0; column < 4; ++column) + { + if (mRegisterMap[row][column]) + { + contiguousSpace[column] = 0; + } + else + { + contiguousSpace[column]++; + totalSpace[column]++; + + if (contiguousSpace[column] > bestContiguousSpace[column]) + { + bestContiguousSpace[column] = contiguousSpace[column]; + } + } + } + } + + unsigned int bestColumn = 0; + for (unsigned int column = 1; column < 4; ++column) + { + if (bestContiguousSpace[column] >= varyingRows && + (bestContiguousSpace[bestColumn] < varyingRows || + totalSpace[column] < totalSpace[bestColumn])) + { + bestColumn = column; + } + } + + if (bestContiguousSpace[bestColumn] >= varyingRows) + { + for (unsigned int row = 0; row < maxVaryingVectors; row++) + { + if (isFree(row, bestColumn, varyingRows, 1)) + { + for (unsigned int arrayIndex = 0; arrayIndex < varyingRows; ++arrayIndex) + { + // If varyingRows > 1, it must be an array. + PackedVaryingRegister registerInfo; + registerInfo.packedVarying = &packedVarying; + registerInfo.registerRow = row + arrayIndex; + registerInfo.registerColumn = bestColumn; + registerInfo.varyingArrayIndex = arrayIndex; + registerInfo.varyingRowIndex = 0; + mRegisterList.push_back(registerInfo); + mRegisterMap[row + arrayIndex][bestColumn] = true; + } + break; + } + } + return true; + } + + return false; +} + +bool VaryingPacking::isFree(unsigned int registerRow, + unsigned int registerColumn, + unsigned int varyingRows, + unsigned int varyingColumns) const +{ + for (unsigned int row = 0; row < varyingRows; ++row) + { + ASSERT(registerRow + row < mRegisterMap.size()); + for (unsigned int column = 0; column < varyingColumns; ++column) + { + ASSERT(registerColumn + column < 4); + if (mRegisterMap[registerRow + row][registerColumn + column]) + { + return false; + } + } + } + + return true; +} + +void VaryingPacking::insert(unsigned int registerRow, + unsigned int registerColumn, + const PackedVarying &packedVarying) +{ + unsigned int varyingRows = 0; + unsigned int varyingColumns = 0; + + const auto &varying = *packedVarying.varying; + ASSERT(!varying.isStruct()); + GLenum transposedType = gl::TransposeMatrixType(varying.type); + varyingRows = gl::VariableRowCount(transposedType); + varyingColumns = gl::VariableColumnCount(transposedType); + + PackedVaryingRegister registerInfo; + registerInfo.packedVarying = &packedVarying; + registerInfo.registerColumn = registerColumn; + + for (unsigned int arrayElement = 0; arrayElement < varying.elementCount(); ++arrayElement) + { + for (unsigned int varyingRow = 0; varyingRow < varyingRows; ++varyingRow) + { + registerInfo.registerRow = registerRow + (arrayElement * varyingRows) + varyingRow; + registerInfo.varyingRowIndex = varyingRow; + registerInfo.varyingArrayIndex = arrayElement; + mRegisterList.push_back(registerInfo); + + for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex) + { + mRegisterMap[registerInfo.registerRow][registerColumn + columnIndex] = true; + } + } + } +} + +// See comment on packVarying. +bool VaryingPacking::packVaryings(gl::InfoLog &infoLog, + const std::vector &packedVaryings, + const std::vector &transformFeedbackVaryings) +{ + std::set uniqueVaryingNames; + + // "Variables are packed into the registers one at a time so that they each occupy a contiguous + // subrectangle. No splitting of variables is permitted." + for (const PackedVarying &packedVarying : packedVaryings) + { + const auto &varying = *packedVarying.varying; + + // Do not assign registers to built-in or unreferenced varyings + if (varying.isBuiltIn() || (!varying.staticUse && !packedVarying.isStructField())) + { + continue; + } + + ASSERT(!varying.isStruct()); + ASSERT(uniqueVaryingNames.count(varying.name) == 0); + + if (packVarying(packedVarying)) + { + uniqueVaryingNames.insert(varying.name); + } + else + { + infoLog << "Could not pack varying " << varying.name; + return false; + } + } + + for (const std::string &transformFeedbackVaryingName : transformFeedbackVaryings) + { + if (transformFeedbackVaryingName.compare(0, 3, "gl_") == 0) + { + // do not pack builtin XFB varyings + continue; + } + + for (const PackedVarying &packedVarying : packedVaryings) + { + const auto &varying = *packedVarying.varying; + + // Make sure transform feedback varyings aren't optimized out. + if (uniqueVaryingNames.count(transformFeedbackVaryingName) == 0) + { + bool found = false; + if (transformFeedbackVaryingName == varying.name) + { + if (!packVarying(packedVarying)) + { + infoLog << "Could not pack varying " << varying.name; + return false; + } + + found = true; + break; + } + if (!found) + { + infoLog << "Transform feedback varying " << transformFeedbackVaryingName + << " does not exist in the vertex shader."; + return false; + } + } + } + } + + // Sort the packed register list + std::sort(mRegisterList.begin(), mRegisterList.end()); + + // Assign semantic indices + for (unsigned int semanticIndex = 0; + semanticIndex < static_cast(mRegisterList.size()); ++semanticIndex) + { + mRegisterList[semanticIndex].semanticIndex = semanticIndex; + } + + return true; +} + +unsigned int VaryingPacking::getRegisterCount() const +{ + unsigned int count = 0; + + for (const Register ® : mRegisterMap) + { + if (reg.data[0] || reg.data[1] || reg.data[2] || reg.data[3]) + { + ++count; + } + } + + if (mBuiltinInfo[SHADER_PIXEL].glFragCoord.enabled) + { + ++count; + } + + if (mBuiltinInfo[SHADER_PIXEL].glPointCoord.enabled) + { + ++count; + } + + return count; +} + +void VaryingPacking::enableBuiltins(ShaderType shaderType, + const ProgramD3DMetadata &programMetadata) +{ + int majorShaderModel = programMetadata.getRendererMajorShaderModel(); + bool position = programMetadata.usesTransformFeedbackGLPosition(); + bool fragCoord = programMetadata.usesFragCoord(); + bool pointCoord = shaderType == SHADER_VERTEX ? programMetadata.addsPointCoordToVertexShader() + : programMetadata.usesPointCoord(); + bool pointSize = programMetadata.usesSystemValuePointSize(); + bool hlsl4 = (majorShaderModel >= 4); + const std::string &userSemantic = GetVaryingSemantic(majorShaderModel, pointSize); + + unsigned int reservedSemanticIndex = getMaxSemanticIndex(); + + BuiltinInfo *builtins = &mBuiltinInfo[shaderType]; + + if (hlsl4) + { + builtins->dxPosition.enableSystem("SV_Position"); + } + else if (shaderType == SHADER_PIXEL) + { + builtins->dxPosition.enableSystem("VPOS"); + } + else + { + builtins->dxPosition.enableSystem("POSITION"); + } + + if (position) + { + builtins->glPosition.enable(userSemantic, reservedSemanticIndex++); + } + + if (fragCoord) + { + builtins->glFragCoord.enable(userSemantic, reservedSemanticIndex++); + } + + if (pointCoord) + { + // SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord) + // In D3D11 we manually compute gl_PointCoord in the GS. + if (hlsl4) + { + builtins->glPointCoord.enable(userSemantic, reservedSemanticIndex++); + } + else + { + builtins->glPointCoord.enable("TEXCOORD", 0); + } + } + + // Special case: do not include PSIZE semantic in HLSL 3 pixel shaders + if (pointSize && (shaderType != SHADER_PIXEL || hlsl4)) + { + builtins->glPointSize.enableSystem("PSIZE"); + } +} + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.h b/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.h new file mode 100644 index 000000000000..ca4640b00020 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/VaryingPacking.h @@ -0,0 +1,175 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// VaryingPacking: +// Class which describes a mapping from varyings to registers in D3D +// for linking between shader stages. +// + +#ifndef LIBANGLE_RENDERER_D3D_VARYINGPACKING_H_ +#define LIBANGLE_RENDERER_D3D_VARYINGPACKING_H_ + +#include "libANGLE/renderer/d3d/RendererD3D.h" + +namespace rx +{ +class ProgramD3DMetadata; + +struct PackedVarying +{ + PackedVarying(const sh::ShaderVariable &varyingIn, sh::InterpolationType interpolationIn) + : varying(&varyingIn), vertexOnly(false), interpolation(interpolationIn) + { + } + PackedVarying(const sh::ShaderVariable &varyingIn, + sh::InterpolationType interpolationIn, + const std::string &parentStructNameIn) + : varying(&varyingIn), + vertexOnly(false), + interpolation(interpolationIn), + parentStructName(parentStructNameIn) + { + } + + bool isStructField() const { return !parentStructName.empty(); } + + const sh::ShaderVariable *varying; + + // Transform feedback varyings can be only referenced in the VS. + bool vertexOnly; + + // Cached so we can store sh::ShaderVariable to point to varying fields. + sh::InterpolationType interpolation; + + // Struct name + std::string parentStructName; +}; + +struct PackedVaryingRegister final +{ + PackedVaryingRegister() + : packedVarying(nullptr), + varyingArrayIndex(0), + varyingRowIndex(0), + registerRow(0), + registerColumn(0) + { + } + + PackedVaryingRegister(const PackedVaryingRegister &) = default; + PackedVaryingRegister &operator=(const PackedVaryingRegister &) = default; + + bool operator<(const PackedVaryingRegister &other) const + { + return sortOrder() < other.sortOrder(); + } + + unsigned int sortOrder() const + { + // TODO(jmadill): Handle interpolation types + return registerRow * 4 + registerColumn; + } + + bool isStructField() const { return !structFieldName.empty(); } + + // Index to the array of varyings. + const PackedVarying *packedVarying; + + // The array element of the packed varying. + unsigned int varyingArrayIndex; + + // The row of the array element of the packed varying. + unsigned int varyingRowIndex; + + // The register row to which we've assigned this packed varying. + unsigned int registerRow; + + // The column of the register row into which we've packed this varying. + unsigned int registerColumn; + + // Assigned after packing + unsigned int semanticIndex; + + // Struct member this varying corresponds to. + std::string structFieldName; +}; + +class VaryingPacking final : angle::NonCopyable +{ + public: + VaryingPacking(GLuint maxVaryingVectors); + + bool packVaryings(gl::InfoLog &infoLog, + const std::vector &packedVaryings, + const std::vector &transformFeedbackVaryings); + + struct Register + { + Register() { data[0] = data[1] = data[2] = data[3] = false; } + + bool &operator[](unsigned int index) { return data[index]; } + bool operator[](unsigned int index) const { return data[index]; } + + bool data[4]; + }; + + Register &operator[](unsigned int index) { return mRegisterMap[index]; } + const Register &operator[](unsigned int index) const { return mRegisterMap[index]; } + + const std::vector &getRegisterList() const { return mRegisterList; } + unsigned int getMaxSemanticIndex() const + { + return static_cast(mRegisterList.size()); + } + unsigned int getRegisterCount() const; + + void enableBuiltins(ShaderType shaderType, const ProgramD3DMetadata &programMetadata); + + struct BuiltinVarying final : angle::NonCopyable + { + BuiltinVarying(); + + std::string str() const; + void enableSystem(const std::string &systemValueSemantic); + void enable(const std::string &semanticVal, unsigned int indexVal); + + bool enabled; + std::string semantic; + unsigned int index; + bool systemValue; + }; + + struct BuiltinInfo + { + BuiltinVarying dxPosition; + BuiltinVarying glPosition; + BuiltinVarying glFragCoord; + BuiltinVarying glPointCoord; + BuiltinVarying glPointSize; + }; + + const BuiltinInfo &builtins(ShaderType shaderType) const { return mBuiltinInfo[shaderType]; } + + bool usesPointSize() const { return mBuiltinInfo[SHADER_VERTEX].glPointSize.enabled; } + + private: + bool packVarying(const PackedVarying &packedVarying); + bool isFree(unsigned int registerRow, + unsigned int registerColumn, + unsigned int varyingRows, + unsigned int varyingColumns) const; + void insert(unsigned int registerRow, + unsigned int registerColumn, + const PackedVarying &packedVarying); + + std::vector mRegisterMap; + std::vector mRegisterList; + + std::vector mBuiltinInfo; +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_D3D_VARYINGPACKING_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp b/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp index 51ea6ef475b1..9efee9db7ca9 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp @@ -243,7 +243,7 @@ gl::Error StreamingVertexBufferInterface::reserveSpace(unsigned int size) } StaticVertexBufferInterface::StaticVertexBufferInterface(BufferFactoryD3D *factory) - : VertexBufferInterface(factory, false) + : VertexBufferInterface(factory, false), mIsCommitted(false) { } @@ -255,13 +255,14 @@ bool StaticVertexBufferInterface::lookupAttribute(const gl::VertexAttribute &att { for (unsigned int element = 0; element < mCache.size(); element++) { - if (mCache[element].type == attrib.type && - mCache[element].size == attrib.size && - mCache[element].stride == ComputeVertexAttributeStride(attrib) && + size_t attribStride = ComputeVertexAttributeStride(attrib); + + if (mCache[element].type == attrib.type && mCache[element].size == attrib.size && + mCache[element].stride == attribStride && mCache[element].normalized == attrib.normalized && mCache[element].pureInteger == attrib.pureInteger) { - size_t offset = (static_cast(attrib.offset) % ComputeVertexAttributeStride(attrib)); + size_t offset = (static_cast(attrib.offset) % attribStride); if (mCache[element].attributeOffset == offset) { if (outStreamOffset) @@ -321,4 +322,11 @@ gl::Error StaticVertexBufferInterface::storeVertexAttributes(const gl::VertexAtt return gl::Error(GL_NO_ERROR); } +void StaticVertexBufferInterface::commit() +{ + if (getBufferSize() > 0) + { + mIsCommitted = true; + } +} } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.h b/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.h index 81a90f682457..692b6ac5062c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/VertexBuffer.h @@ -135,6 +135,11 @@ class StaticVertexBufferInterface : public VertexBufferInterface bool lookupAttribute(const gl::VertexAttribute &attribute, unsigned int* outStreamFffset); + // If a static vertex buffer is committed then no more attribute data can be added to it + // A new static vertex buffer should be created instead + void commit(); + bool isCommitted() { return mIsCommitted; } + protected: gl::Error reserveSpace(unsigned int size); @@ -151,6 +156,7 @@ class StaticVertexBufferInterface : public VertexBufferInterface unsigned int streamOffset; }; + bool mIsCommitted; std::vector mCache; }; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp index 559e633eaa8d..b392d0f4da25 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp @@ -88,10 +88,17 @@ void VertexDataManager::hintUnmapAllResources(const std::vectorattribute->buffer.get(); BufferD3D *storage = buffer ? GetImplAs(buffer) : nullptr; - StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : nullptr; + StaticVertexBufferInterface *staticBuffer = + storage + ? storage->getStaticVertexBuffer(*translated->attribute, D3D_BUFFER_DO_NOT_CREATE) + : nullptr; if (staticBuffer) { + // Commit all the static vertex buffers. This fixes them in size/contents, and forces + // ANGLE to use a new static buffer (or recreate the static buffers) next time + staticBuffer->commit(); + staticBuffer->getVertexBuffer()->hintUnmapResource(); } } @@ -146,10 +153,14 @@ gl::Error VertexDataManager::prepareVertexData(const gl::State &state, { mActiveEnabledAttributes.push_back(translated); - // Also invalidate static buffers that don't contain matching attributes - invalidateMatchingStaticData( - vertexAttributes[attribIndex], - state.getVertexAttribCurrentValue(static_cast(attribIndex))); + gl::Buffer *buffer = vertexAttributes[attribIndex].buffer.get(); + if (buffer) + { + // Also reinitialize static buffers which didn't contain matching data + // last time they were used + BufferD3D *bufferImpl = GetImplAs(buffer); + bufferImpl->reinitOutOfDateStaticData(); + } } else { @@ -215,26 +226,6 @@ gl::Error VertexDataManager::prepareVertexData(const gl::State &state, return gl::Error(GL_NO_ERROR); } -void VertexDataManager::invalidateMatchingStaticData(const gl::VertexAttribute &attrib, - const gl::VertexAttribCurrentValueData ¤tValue) const -{ - gl::Buffer *buffer = attrib.buffer.get(); - - if (buffer) - { - BufferD3D *bufferImpl = GetImplAs(buffer); - StaticVertexBufferInterface *staticBuffer = bufferImpl->getStaticVertexBuffer(); - - if (staticBuffer && - staticBuffer->getBufferSize() > 0 && - !staticBuffer->lookupAttribute(attrib, NULL) && - !staticBuffer->directStoragePossible(attrib, currentValue.Type)) - { - bufferImpl->invalidateStaticData(); - } - } -} - gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &translatedAttrib, GLsizei count, GLsizei instances) const @@ -242,7 +233,9 @@ gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &tr const gl::VertexAttribute &attrib = *translatedAttrib.attribute; gl::Buffer *buffer = attrib.buffer.get(); BufferD3D *bufferImpl = buffer ? GetImplAs(buffer) : NULL; - StaticVertexBufferInterface *staticBuffer = bufferImpl ? bufferImpl->getStaticVertexBuffer() : NULL; + StaticVertexBufferInterface *staticBuffer = + bufferImpl ? bufferImpl->getStaticVertexBuffer(attrib, D3D_BUFFER_CREATE_IF_NECESSARY) + : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast(mStreamingBuffer); if (!vertexBuffer->directStoragePossible(attrib, translatedAttrib.currentValueType)) @@ -291,12 +284,13 @@ gl::Error VertexDataManager::storeAttribute(TranslatedAttribute *translated, ASSERT(attrib.enabled); BufferD3D *storage = buffer ? GetImplAs(buffer) : NULL; - StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : NULL; + StaticVertexBufferInterface *staticBuffer = + storage ? storage->getStaticVertexBuffer(attrib, D3D_BUFFER_DO_NOT_CREATE) : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast(mStreamingBuffer); bool directStorage = vertexBuffer->directStoragePossible(attrib, translated->currentValueType); // Instanced vertices do not apply the 'start' offset - GLint firstVertexIndex = (instances > 0 && attrib.divisor > 0 ? 0 : start); + GLint firstVertexIndex = (attrib.divisor > 0 ? 0 : start); translated->vertexBuffer = vertexBuffer->getVertexBuffer(); @@ -363,7 +357,8 @@ gl::Error VertexDataManager::storeAttribute(TranslatedAttribute *translated, (static_cast(attrib.offset) / static_cast(ComputeVertexAttributeStride(attrib))) * outputElementSize; - unsigned int startOffset = (instances == 0 || attrib.divisor == 0) ? firstVertexIndex * outputElementSize : 0; + ASSERT(attrib.divisor == 0 || firstVertexIndex == 0); + unsigned int startOffset = firstVertexIndex * outputElementSize; if (streamOffset + firstElementOffset + startOffset < streamOffset) { return gl::Error(GL_OUT_OF_MEMORY); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.h b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.h index f03c777bee36..fb349c4cc2e2 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.h @@ -82,9 +82,6 @@ class VertexDataManager : angle::NonCopyable GLsizei count, GLsizei instances) const; - void invalidateMatchingStaticData(const gl::VertexAttribute &attrib, - const gl::VertexAttribCurrentValueData ¤tValue) const; - gl::Error storeAttribute(TranslatedAttribute *translated, GLint start, GLsizei count, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp index 3b42c9817771..0c1ce233d95b 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp @@ -213,21 +213,56 @@ D3D11_INPUT_ELEMENT_DESC quad3DLayout[] = Blit11::Blit11(Renderer11 *renderer) : mRenderer(renderer), + mResourcesInitialized(false), mVertexBuffer(nullptr), mPointSampler(nullptr), mLinearSampler(nullptr), mScissorEnabledRasterizerState(nullptr), mScissorDisabledRasterizerState(nullptr), mDepthStencilState(nullptr), - mQuad2DIL(quad2DLayout, ArraySize(quad2DLayout), g_VS_Passthrough2D, ArraySize(g_VS_Passthrough2D), "Blit11 2D input layout"), + mQuad2DIL(quad2DLayout, + ArraySize(quad2DLayout), + g_VS_Passthrough2D, + ArraySize(g_VS_Passthrough2D), + "Blit11 2D input layout"), mQuad2DVS(g_VS_Passthrough2D, ArraySize(g_VS_Passthrough2D), "Blit11 2D vertex shader"), - mDepthPS(g_PS_PassthroughDepth2D, ArraySize(g_PS_PassthroughDepth2D), "Blit11 2D depth pixel shader"), - mQuad3DIL(quad3DLayout, ArraySize(quad3DLayout), g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), "Blit11 3D input layout"), + mDepthPS(g_PS_PassthroughDepth2D, + ArraySize(g_PS_PassthroughDepth2D), + "Blit11 2D depth pixel shader"), + mQuad3DIL(quad3DLayout, + ArraySize(quad3DLayout), + g_VS_Passthrough3D, + ArraySize(g_VS_Passthrough3D), + "Blit11 3D input layout"), mQuad3DVS(g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), "Blit11 3D vertex shader"), mQuad3DGS(g_GS_Passthrough3D, ArraySize(g_GS_Passthrough3D), "Blit11 3D geometry shader"), mSwizzleCB(nullptr) { - TRACE_EVENT0("gpu.angle", "Blit11::Blit11"); +} + +Blit11::~Blit11() +{ + freeResources(); + + mQuad2DIL.release(); + mQuad2DVS.release(); + mDepthPS.release(); + + mQuad3DIL.release(); + mQuad3DVS.release(); + mQuad3DGS.release(); + + clearShaderMap(); +} + +gl::Error Blit11::initResources() +{ + if (mResourcesInitialized) + { + return gl::Error(GL_NO_ERROR); + } + + TRACE_EVENT0("gpu.angle", "Blit11::initResources"); HRESULT result; ID3D11Device *device = mRenderer->getDevice(); @@ -236,7 +271,7 @@ Blit11::Blit11(Renderer11 *renderer) vbDesc.ByteWidth = static_cast(std::max(sizeof(d3d11::PositionLayerTexCoord3DVertex), sizeof(d3d11::PositionTexCoordVertex)) * - 6 * renderer->getRendererCaps().max3DTextureSize); + 6 * mRenderer->getRendererCaps().max3DTextureSize); vbDesc.Usage = D3D11_USAGE_DYNAMIC; vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; @@ -245,6 +280,12 @@ Blit11::Blit11(Renderer11 *renderer) result = device->CreateBuffer(&vbDesc, nullptr, &mVertexBuffer); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, "Failed to create blit vertex buffer, HRESULT: 0x%X", + result); + } d3d11::SetDebugName(mVertexBuffer, "Blit11 vertex buffer"); D3D11_SAMPLER_DESC pointSamplerDesc; @@ -264,6 +305,12 @@ Blit11::Blit11(Renderer11 *renderer) result = device->CreateSamplerState(&pointSamplerDesc, &mPointSampler); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create blit point sampler state, HRESULT: 0x%X", result); + } d3d11::SetDebugName(mPointSampler, "Blit11 point sampler"); D3D11_SAMPLER_DESC linearSamplerDesc; @@ -283,6 +330,12 @@ Blit11::Blit11(Renderer11 *renderer) result = device->CreateSamplerState(&linearSamplerDesc, &mLinearSampler); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create blit linear sampler state, HRESULT: 0x%X", result); + } d3d11::SetDebugName(mLinearSampler, "Blit11 linear sampler"); // Use a rasterizer state that will not cull so that inverted quads will not be culled @@ -300,11 +353,25 @@ Blit11::Blit11(Renderer11 *renderer) rasterDesc.ScissorEnable = TRUE; result = device->CreateRasterizerState(&rasterDesc, &mScissorEnabledRasterizerState); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create blit scissoring rasterizer state, HRESULT: 0x%X", + result); + } d3d11::SetDebugName(mScissorEnabledRasterizerState, "Blit11 scissoring rasterizer state"); rasterDesc.ScissorEnable = FALSE; result = device->CreateRasterizerState(&rasterDesc, &mScissorDisabledRasterizerState); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create blit no scissoring rasterizer state, HRESULT: 0x%X", + result); + } d3d11::SetDebugName(mScissorDisabledRasterizerState, "Blit11 no scissoring rasterizer state"); D3D11_DEPTH_STENCIL_DESC depthStencilDesc; @@ -325,6 +392,12 @@ Blit11::Blit11(Renderer11 *renderer) result = device->CreateDepthStencilState(&depthStencilDesc, &mDepthStencilState); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, + "Failed to create blit depth stencil state, HRESULT: 0x%X", result); + } d3d11::SetDebugName(mDepthStencilState, "Blit11 depth stencil state"); D3D11_BUFFER_DESC swizzleBufferDesc; @@ -337,10 +410,20 @@ Blit11::Blit11(Renderer11 *renderer) result = device->CreateBuffer(&swizzleBufferDesc, nullptr, &mSwizzleCB); ASSERT(SUCCEEDED(result)); + if (FAILED(result)) + { + freeResources(); + return gl::Error(GL_OUT_OF_MEMORY, "Failed to create blit swizzle buffer, HRESULT: 0x%X", + result); + } d3d11::SetDebugName(mSwizzleCB, "Blit11 swizzle constant buffer"); + + mResourcesInitialized = true; + + return gl::Error(GL_NO_ERROR); } -Blit11::~Blit11() +void Blit11::freeResources() { SafeRelease(mVertexBuffer); SafeRelease(mPointSampler); @@ -348,18 +431,9 @@ Blit11::~Blit11() SafeRelease(mScissorEnabledRasterizerState); SafeRelease(mScissorDisabledRasterizerState); SafeRelease(mDepthStencilState); - - mQuad2DIL.release(); - mQuad2DVS.release(); - mDepthPS.release(); - - mQuad3DIL.release(); - mQuad3DVS.release(); - mQuad3DGS.release(); - SafeRelease(mSwizzleCB); - clearShaderMap(); + mResourcesInitialized = false; } // static @@ -521,6 +595,12 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, GLenum swizzleBlue, GLenum swizzleAlpha) { + gl::Error error = initResources(); + if (error.isError()) + { + return error; + } + HRESULT result; ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); @@ -550,7 +630,7 @@ gl::Error Blit11::swizzleTexture(ID3D11ShaderResourceView *source, } const Shader *shader = nullptr; - gl::Error error = getSwizzleShader(shaderType, sourceSRVDesc.ViewDimension, &shader); + error = getSwizzleShader(shaderType, sourceSRVDesc.ViewDimension, &shader); if (error.isError()) { return error; @@ -653,6 +733,12 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize, const gl::Rectangle *scissor, GLenum destFormat, GLenum filter) { + gl::Error error = initResources(); + if (error.isError()) + { + return error; + } + HRESULT result; ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); @@ -668,7 +754,7 @@ gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &s ShaderDimension dimension = (sourceSRVDesc.ViewDimension == D3D11_SRV_DIMENSION_TEXTURE3D) ? SHADER_3D : SHADER_2D; const Shader *shader = nullptr; - gl::Error error = getBlitShader(destFormat, isSigned, dimension, &shader); + error = getBlitShader(destFormat, isSigned, dimension, &shader); if (error.isError()) { return error; @@ -787,6 +873,12 @@ gl::Error Blit11::copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sou ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize, const gl::Rectangle *scissor) { + gl::Error error = initResources(); + if (error.isError()) + { + return error; + } + HRESULT result; ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); @@ -898,6 +990,12 @@ gl::Error Blit11::copyDepthStencil(ID3D11Resource *source, unsigned int sourceSu ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize, const gl::Rectangle *scissor, bool stencilOnly) { + gl::Error error = initResources(); + if (error.isError()) + { + return error; + } + ID3D11Device *device = mRenderer->getDevice(); ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h index d3be7917c69d..b64183a58131 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h @@ -125,6 +125,9 @@ class Blit11 : angle::NonCopyable WriteVertexFunction vertexWriteFunction; }; + gl::Error initResources(); + void freeResources(); + ShaderSupport getShaderSupport(const Shader &shader); static BlitShaderType GetBlitShaderType(GLenum destinationFormat, bool isSigned, ShaderDimension dimension); @@ -148,6 +151,7 @@ class Blit11 : angle::NonCopyable std::map mBlitShaderMap; std::map mSwizzleShaderMap; + bool mResourcesInitialized; ID3D11Buffer *mVertexBuffer; ID3D11SamplerState *mPointSampler; ID3D11SamplerState *mLinearSampler; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp index 670561afb1de..7ce85f2a0a58 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp @@ -361,7 +361,7 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset) } mSize = std::max(mSize, requiredSize); - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); return gl::Error(GL_NO_ERROR); } @@ -417,7 +417,7 @@ gl::Error Buffer11::copySubData(BufferImpl *source, copyDest->setDataRevision(copyDest->getDataRevision() + 1); mSize = std::max(mSize, destOffset + size); - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); return gl::Error(GL_NO_ERROR); } @@ -457,7 +457,7 @@ gl::Error Buffer11::mapRange(size_t offset, size_t length, GLbitfield access, GL { // Update the data revision immediately, since the data might be changed at any time mMappedStorage->setDataRevision(mMappedStorage->getDataRevision() + 1); - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); } uint8_t *mappedBuffer = mMappedStorage->map(offset, length, access); @@ -492,7 +492,7 @@ void Buffer11::markTransformFeedbackUsage() transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1); } - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); } void Buffer11::markBufferUsage() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp index da4cf3947dda..f9d28e840828 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp @@ -39,32 +39,53 @@ void DebugAnnotator11::beginEvent(const wchar_t *eventName) { initializeDevice(); - mUserDefinedAnnotation->BeginEvent(eventName); + if (mUserDefinedAnnotation != nullptr) + { + mUserDefinedAnnotation->BeginEvent(eventName); + } } void DebugAnnotator11::endEvent() { initializeDevice(); - mUserDefinedAnnotation->EndEvent(); + if (mUserDefinedAnnotation != nullptr) + { + mUserDefinedAnnotation->EndEvent(); + } } void DebugAnnotator11::setMarker(const wchar_t *markerName) { initializeDevice(); - mUserDefinedAnnotation->SetMarker(markerName); + if (mUserDefinedAnnotation != nullptr) + { + mUserDefinedAnnotation->SetMarker(markerName); + } } bool DebugAnnotator11::getStatus() { - // ID3DUserDefinedAnnotation::GetStatus doesn't work with the Graphics Diagnostics tools in Visual Studio 2013. +#if defined(ANGLE_ENABLE_WINDOWS_STORE) +#if (NTDDI_VERSION == NTDDI_WIN10) + initializeDevice(); + + if (mUserDefinedAnnotation != nullptr) + { + return !!(mUserDefinedAnnotation->GetStatus()); + } + + return true; // Default if initializeDevice() failed +#elif defined(_DEBUG) static bool underCapture = true; -#if defined(_DEBUG) && defined(ANGLE_ENABLE_WINDOWS_STORE) - // In the Windows Store, we can use IDXGraphicsAnalysis. The call to GetDebugInterface1 only succeeds if the app is under capture. + // ID3DUserDefinedAnnotation::GetStatus doesn't work with the Graphics Diagnostics tools in + // Windows 8.1/Visual Studio 2013. We can use IDXGraphicsAnalysis, though. + // The call to GetDebugInterface1 only succeeds if the app is under capture. // This should only be called in DEBUG mode. - // If an app links against DXGIGetDebugInterface1 in release mode then it will fail Windows Store ingestion checks. + // If an app links against DXGIGetDebugInterface1 in release mode then it will fail Windows + // Store ingestion checks. // Cache the result to reduce the number of calls to DXGIGetDebugInterface1 static bool triedIDXGraphicsAnalysis = false; @@ -82,9 +103,16 @@ bool DebugAnnotator11::getStatus() SafeRelease(graphicsAnalysis); triedIDXGraphicsAnalysis = true; } -#endif // _DEBUG && !ANGLE_ENABLE_WINDOWS_STORE return underCapture; +#else + // We can't detect GetStatus() on release WinRT 8.1 builds, so always return true. + return true; +#endif // (NTDDI_VERSION == NTDDI_WIN10) or _DEBUG +#else + // We can't detect GetStatus() on desktop ANGLE builds so always return true. + return true; +#endif // ANGLE_ENABLE_WINDOWS_STORE } void DebugAnnotator11::initializeDevice() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp index 5868612717b0..aafd598b459c 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp @@ -500,6 +500,10 @@ gl::Error InputLayoutCache::applyVertexBuffers(const std::vector(i); + } + dest[count] = 0; } +template +void CopyLineLoopIndices(const GLvoid *indices, GLuint *dest, size_t count) +{ + const T *srcPtr = static_cast(indices); + for (size_t i = 0; i < count; ++i) + { + dest[i] = static_cast(srcPtr[i]); + } + dest[count] = static_cast(srcPtr[0]); +} + +void SetTriangleFanIndices(GLuint *destPtr, size_t numTris) +{ + for (size_t i = 0; i < numTris; i++) + { + destPtr[i * 3 + 0] = 0; + destPtr[i * 3 + 1] = static_cast(i) + 1; + destPtr[i * 3 + 2] = static_cast(i) + 2; + } +} + +template +void CopyLineLoopIndicesWithRestart(const GLvoid *indices, + size_t count, + GLenum indexType, + std::vector *bufferOut) +{ + GLuint restartIndex = gl::GetPrimitiveRestartIndex(indexType); + GLuint d3dRestartIndex = static_cast(d3d11::GetPrimitiveRestartIndex()); + const T *srcPtr = static_cast(indices); + Optional currentLoopStart; + + bufferOut->clear(); + + for (size_t indexIdx = 0; indexIdx < count; ++indexIdx) + { + GLuint value = static_cast(srcPtr[indexIdx]); + + if (value == restartIndex) + { + if (currentLoopStart.valid()) + { + bufferOut->push_back(currentLoopStart.value()); + bufferOut->push_back(d3dRestartIndex); + currentLoopStart.reset(); + } + } + else + { + bufferOut->push_back(value); + if (!currentLoopStart.valid()) + { + currentLoopStart = value; + } + } + } + + if (currentLoopStart.valid()) + { + bufferOut->push_back(currentLoopStart.value()); + } +} + +void GetLineLoopIndices(const GLvoid *indices, + GLenum indexType, + GLuint count, + bool usePrimitiveRestartFixedIndex, + std::vector *bufferOut) +{ + if (indexType != GL_NONE && usePrimitiveRestartFixedIndex) + { + switch (indexType) + { + case GL_UNSIGNED_BYTE: + CopyLineLoopIndicesWithRestart(indices, count, indexType, bufferOut); + break; + case GL_UNSIGNED_SHORT: + CopyLineLoopIndicesWithRestart(indices, count, indexType, bufferOut); + break; + case GL_UNSIGNED_INT: + CopyLineLoopIndicesWithRestart(indices, count, indexType, bufferOut); + break; + default: + UNREACHABLE(); + break; + } + return; + } + + // For non-primitive-restart draws, the index count is static. + bufferOut->resize(static_cast(count) + 1); + + switch (indexType) + { + // Non-indexed draw + case GL_NONE: + SetLineLoopIndices(&(*bufferOut)[0], count); + break; + case GL_UNSIGNED_BYTE: + CopyLineLoopIndices(indices, &(*bufferOut)[0], count); + break; + case GL_UNSIGNED_SHORT: + CopyLineLoopIndices(indices, &(*bufferOut)[0], count); + break; + case GL_UNSIGNED_INT: + CopyLineLoopIndices(indices, &(*bufferOut)[0], count); + break; + default: + UNREACHABLE(); + break; + } +} + +template +void CopyTriangleFanIndices(const GLvoid *indices, GLuint *destPtr, size_t numTris) +{ + const T *srcPtr = static_cast(indices); + + for (size_t i = 0; i < numTris; i++) + { + destPtr[i * 3 + 0] = static_cast(srcPtr[0]); + destPtr[i * 3 + 1] = static_cast(srcPtr[i + 1]); + destPtr[i * 3 + 2] = static_cast(srcPtr[i + 2]); + } +} + +template +void CopyTriangleFanIndicesWithRestart(const GLvoid *indices, + GLuint indexCount, + GLenum indexType, + std::vector *bufferOut) +{ + GLuint restartIndex = gl::GetPrimitiveRestartIndex(indexType); + GLuint d3dRestartIndex = gl::GetPrimitiveRestartIndex(GL_UNSIGNED_INT); + const T *srcPtr = static_cast(indices); + Optional vertexA; + Optional vertexB; + + bufferOut->clear(); + + for (size_t indexIdx = 0; indexIdx < indexCount; ++indexIdx) + { + GLuint value = static_cast(srcPtr[indexIdx]); + + if (value == restartIndex) + { + bufferOut->push_back(d3dRestartIndex); + vertexA.reset(); + vertexB.reset(); + } + else + { + if (!vertexA.valid()) + { + vertexA = value; + } + else if (!vertexB.valid()) + { + vertexB = value; + } + else + { + bufferOut->push_back(vertexA.value()); + bufferOut->push_back(vertexB.value()); + bufferOut->push_back(value); + vertexB = value; + } + } + } +} + +void GetTriFanIndices(const GLvoid *indices, + GLenum indexType, + GLuint count, + bool usePrimitiveRestartFixedIndex, + std::vector *bufferOut) +{ + if (indexType != GL_NONE && usePrimitiveRestartFixedIndex) + { + switch (indexType) + { + case GL_UNSIGNED_BYTE: + CopyTriangleFanIndicesWithRestart(indices, count, indexType, bufferOut); + break; + case GL_UNSIGNED_SHORT: + CopyTriangleFanIndicesWithRestart(indices, count, indexType, bufferOut); + break; + case GL_UNSIGNED_INT: + CopyTriangleFanIndicesWithRestart(indices, count, indexType, bufferOut); + break; + default: + UNREACHABLE(); + break; + } + return; + } + + // For non-primitive-restart draws, the index count is static. + GLuint numTris = count - 2; + bufferOut->resize(numTris * 3); + + switch (indexType) + { + // Non-indexed draw + case GL_NONE: + SetTriangleFanIndices(&(*bufferOut)[0], numTris); + break; + case GL_UNSIGNED_BYTE: + CopyTriangleFanIndices(indices, &(*bufferOut)[0], numTris); + break; + case GL_UNSIGNED_SHORT: + CopyTriangleFanIndices(indices, &(*bufferOut)[0], numTris); + break; + case GL_UNSIGNED_INT: + CopyTriangleFanIndices(indices, &(*bufferOut)[0], numTris); + break; + default: + UNREACHABLE(); + break; + } +} + +} // anonymous namespace + void Renderer11::SRVCache::update(size_t resourceIndex, ID3D11ShaderResourceView *srv) { ASSERT(resourceIndex < mCurrentSRVs.size()); @@ -238,6 +470,8 @@ void Renderer11::SRVCache::clear() Renderer11::Renderer11(egl::Display *display) : RendererD3D(display), mStateCache(this), + mCurStencilSize(0), + mStateManager(this), mLastHistogramUpdateTime(ANGLEPlatformCurrent()->monotonicallyIncreasingTime()), mDebug(nullptr) { @@ -1145,50 +1379,17 @@ gl::Error Renderer11::setRasterizerState(const gl::RasterizerState &rasterState) return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, +void Renderer11::syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) +{ + mStateManager.syncState(state, bitmask); +} + +gl::Error Renderer11::setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, unsigned int sampleMask) { - if (mForceSetBlendState || - memcmp(&blendState, &mCurBlendState, sizeof(gl::BlendState)) != 0 || - memcmp(&blendColor, &mCurBlendColor, sizeof(gl::ColorF)) != 0 || - sampleMask != mCurSampleMask) - { - ID3D11BlendState *dxBlendState = NULL; - gl::Error error = mStateCache.getBlendState(framebuffer, blendState, &dxBlendState); - if (error.isError()) - { - return error; - } - - ASSERT(dxBlendState != NULL); - - float blendColors[4] = {0.0f}; - if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && - blendState.destBlendRGB != GL_CONSTANT_ALPHA && blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) - { - blendColors[0] = blendColor.red; - blendColors[1] = blendColor.green; - blendColors[2] = blendColor.blue; - blendColors[3] = blendColor.alpha; - } - else - { - blendColors[0] = blendColor.alpha; - blendColors[1] = blendColor.alpha; - blendColors[2] = blendColor.alpha; - blendColors[3] = blendColor.alpha; - } - - mDeviceContext->OMSetBlendState(dxBlendState, blendColors, sampleMask); - - mCurBlendState = blendState; - mCurBlendColor = blendColor; - mCurSampleMask = sampleMask; - } - - mForceSetBlendState = false; - - return gl::Error(GL_NO_ERROR); + return mStateManager.setBlendState(framebuffer, blendState, blendColor, sampleMask); } gl::Error Renderer11::setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, @@ -1198,9 +1399,17 @@ gl::Error Renderer11::setDepthStencilState(const gl::DepthStencilState &depthSte memcmp(&depthStencilState, &mCurDepthStencilState, sizeof(gl::DepthStencilState)) != 0 || stencilRef != mCurStencilRef || stencilBackRef != mCurStencilBackRef) { - ASSERT(depthStencilState.stencilWritemask == depthStencilState.stencilBackWritemask); + // get the maximum size of the stencil ref + unsigned int maxStencil = 0; + if (depthStencilState.stencilTest && mCurStencilSize > 0) + { + maxStencil = (1 << mCurStencilSize) - 1; + } + ASSERT((depthStencilState.stencilWritemask & maxStencil) == + (depthStencilState.stencilBackWritemask & maxStencil)); ASSERT(stencilRef == stencilBackRef); - ASSERT(depthStencilState.stencilMask == depthStencilState.stencilBackMask); + ASSERT((depthStencilState.stencilMask & maxStencil) == + (depthStencilState.stencilBackMask & maxStencil)); ID3D11DepthStencilState *dxDepthStencilState = NULL; gl::Error error = mStateCache.getDepthStencilState(depthStencilState, &dxDepthStencilState); @@ -1500,6 +1709,13 @@ gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer) unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index); unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index); } + + unsigned int stencilSize = depthStencil->getStencilSize(); + if (!mDepthStencilInitialized || stencilSize != mCurStencilSize) + { + mCurStencilSize = stencilSize; + mForceSetDepthStencilState = true; + } } // Apply the render target and depth stencil @@ -1514,7 +1730,7 @@ gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer) mRenderTargetDesc.format = renderTargetFormat; mForceSetViewport = true; mForceSetScissor = true; - mForceSetBlendState = true; + mStateManager.forceSetBlendState(); if (!mDepthStencilInitialized) { @@ -1557,9 +1773,19 @@ gl::Error Renderer11::applyVertexBuffer(const gl::State &state, GLenum mode, GLi return mInputLayoutCache.applyVertexBuffers(mTranslatedAttribCache, mode, state.getProgram(), sourceInfo); } -gl::Error Renderer11::applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo) +gl::Error Renderer11::applyIndexBuffer(const gl::Data &data, + const GLvoid *indices, + GLsizei count, + GLenum mode, + GLenum type, + TranslatedIndexData *indexInfo, + SourceIndexData *sourceIndexInfo) { - gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, indexInfo, sourceIndexInfo); + gl::VertexArray *vao = data.state->getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, + indexInfo, sourceIndexInfo, + data.state->isPrimitiveRestartEnabled()); if (error.isError()) { return error; @@ -1656,18 +1882,17 @@ void Renderer11::applyTransformFeedbackBuffers(const gl::State &state) gl::Error Renderer11::drawArraysImpl(const gl::Data &data, GLenum mode, GLsizei count, - GLsizei instances, - bool usesPointSize) + GLsizei instances) { - bool useInstancedPointSpriteEmulation = usesPointSize && getWorkarounds().useInstancedPointSpriteEmulation; - if (mode == GL_POINTS && data.state->isTransformFeedbackActiveUnpaused()) - { - // Since point sprites are generated with a geometry shader, too many vertices will - // be written if transform feedback is active. To work around this, draw only the points - // with the stream out shader and no pixel shader to feed the stream out buffers and then - // draw again with the point sprite geometry shader to rasterize the point sprites. + ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); - mDeviceContext->PSSetShader(NULL, NULL, 0); + if (programD3D->usesGeometryShader(mode) && data.state->isTransformFeedbackActiveUnpaused()) + { + // Since we use a geometry if-and-only-if we rewrite vertex streams, transform feedback + // won't get the correct output. To work around this, draw with *only* the stream out + // first (no pixel shader) to feed the stream out buffers and then draw again with the + // geometry shader + pixel shader to rasterize the primitives. + mDeviceContext->PSSetShader(nullptr, nullptr, 0); if (instances > 0) { @@ -1678,62 +1903,41 @@ gl::Error Renderer11::drawArraysImpl(const gl::Data &data, mDeviceContext->Draw(count, 0); } - ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); - - rx::ShaderExecutableD3D *pixelExe = NULL; + rx::ShaderExecutableD3D *pixelExe = nullptr; gl::Error error = programD3D->getPixelExecutableForFramebuffer(data.state->getDrawFramebuffer(), &pixelExe); if (error.isError()) { return error; } - // Skip this step if we're doing rasterizer discard. - if (pixelExe && !data.state->getRasterizerState().rasterizerDiscard && usesPointSize) + // Skip the draw call if rasterizer discard is enabled (or no fragment shader). + if (!pixelExe || data.state->getRasterizerState().rasterizerDiscard) { - ID3D11PixelShader *pixelShader = GetAs(pixelExe)->getPixelShader(); - ASSERT(reinterpret_cast(pixelShader) == mAppliedPixelShader); - mDeviceContext->PSSetShader(pixelShader, NULL, 0); - - // Retrieve the point sprite geometry shader - rx::ShaderExecutableD3D *geometryExe = programD3D->getGeometryExecutable(); - ID3D11GeometryShader *geometryShader = (geometryExe ? GetAs(geometryExe)->getGeometryShader() : NULL); - mAppliedGeometryShader = reinterpret_cast(geometryShader); - ASSERT(geometryShader); - mDeviceContext->GSSetShader(geometryShader, NULL, 0); - - if (instances > 0) - { - mDeviceContext->DrawInstanced(count, instances, 0, 0); - } - else - { - mDeviceContext->Draw(count, 0); - } + return gl::Error(GL_NO_ERROR); } - return gl::Error(GL_NO_ERROR); - } - else if (mode == GL_LINE_LOOP) - { - return drawLineLoop(count, GL_NONE, NULL, 0, NULL); - } - else if (mode == GL_TRIANGLE_FAN) - { - return drawTriangleFan(count, GL_NONE, NULL, 0, NULL, instances); - } - else if (instances > 0) - { - mDeviceContext->DrawInstanced(count, instances, 0, 0); - return gl::Error(GL_NO_ERROR); - } - else - { - // If the shader is writing to gl_PointSize, then pointsprites are being rendered. - // Emulating instanced point sprites for FL9_3 requires the topology to be - // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. - if (mode == GL_POINTS && useInstancedPointSpriteEmulation) + ID3D11PixelShader *pixelShader = GetAs(pixelExe)->getPixelShader(); + ASSERT(reinterpret_cast(pixelShader) == mAppliedPixelShader); + mDeviceContext->PSSetShader(pixelShader, NULL, 0); + + // Retrieve the geometry shader. + rx::ShaderExecutableD3D *geometryExe = nullptr; + error = + programD3D->getGeometryExecutableForPrimitiveType(data, mode, &geometryExe, nullptr); + if (error.isError()) { - mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); + return error; + } + + ID3D11GeometryShader *geometryShader = + (geometryExe ? GetAs(geometryExe)->getGeometryShader() : NULL); + mAppliedGeometryShader = reinterpret_cast(geometryShader); + ASSERT(geometryShader); + mDeviceContext->GSSetShader(geometryShader, NULL, 0); + + if (instances > 0) + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); } else { @@ -1741,61 +1945,101 @@ gl::Error Renderer11::drawArraysImpl(const gl::Data &data, } return gl::Error(GL_NO_ERROR); } + + if (mode == GL_LINE_LOOP) + { + return drawLineLoop(data, count, GL_NONE, nullptr, nullptr, instances); + } + + if (mode == GL_TRIANGLE_FAN) + { + return drawTriangleFan(data, count, GL_NONE, nullptr, 0, instances); + } + + if (instances > 0) + { + mDeviceContext->DrawInstanced(count, instances, 0, 0); + return gl::Error(GL_NO_ERROR); + } + + bool useInstancedPointSpriteEmulation = + programD3D->usesPointSize() && getWorkarounds().useInstancedPointSpriteEmulation; + + // If the shader is writing to gl_PointSize, then pointsprites are being rendered. + // Emulating instanced point sprites for FL9_3 requires the topology to be + // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. + if (mode == GL_POINTS && useInstancedPointSpriteEmulation) + { + mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); + } + else + { + mDeviceContext->Draw(count, 0); + } + return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::drawElementsImpl(GLenum mode, +gl::Error Renderer11::drawElementsImpl(const gl::Data &data, + const TranslatedIndexData &indexInfo, + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, - gl::Buffer *elementArrayBuffer, - const TranslatedIndexData &indexInfo, - GLsizei instances, - bool usesPointSize) + GLsizei instances) { - bool useInstancedPointSpriteEmulation = usesPointSize && getWorkarounds().useInstancedPointSpriteEmulation; int minIndex = static_cast(indexInfo.indexRange.start); if (mode == GL_LINE_LOOP) { - return drawLineLoop(count, type, indices, minIndex, elementArrayBuffer); + return drawLineLoop(data, count, type, indices, &indexInfo, instances); } - else if (mode == GL_TRIANGLE_FAN) + + if (mode == GL_TRIANGLE_FAN) { - return drawTriangleFan(count, type, indices, minIndex, elementArrayBuffer, instances); + return drawTriangleFan(data, count, type, indices, minIndex, instances); } - else if (instances > 0) + + if (instances > 0) { mDeviceContext->DrawIndexedInstanced(count, instances, 0, -minIndex, 0); return gl::Error(GL_NO_ERROR); } + + // If the shader is writing to gl_PointSize, then pointsprites are being rendered. + // Emulating instanced point sprites for FL9_3 requires the topology to be + // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. + const ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); + if (mode == GL_POINTS && programD3D->usesInstancedPointSpriteEmulation()) + { + // The count parameter passed to drawElements represents the total number of instances + // to be rendered. Each instance is referenced by the bound index buffer from the + // the caller. + // + // Indexed pointsprite emulation replicates data for duplicate entries found + // in the index buffer. + // This is not an efficent rendering mechanism and is only used on downlevel renderers + // that do not support geometry shaders. + mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); + } else { - // If the shader is writing to gl_PointSize, then pointsprites are being rendered. - // Emulating instanced point sprites for FL9_3 requires the topology to be - // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. - if (mode == GL_POINTS && useInstancedPointSpriteEmulation) - { - // The count parameter passed to drawElements represents the total number of instances - // to be rendered. Each instance is referenced by the bound index buffer from the - // the caller. - // - // Indexed pointsprite emulation replicates data for duplicate entries found - // in the index buffer. - // This is not an efficent rendering mechanism and is only used on downlevel renderers - // that do not support geometry shaders. - mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); - return gl::Error(GL_NO_ERROR); - } - else - { - mDeviceContext->DrawIndexed(count, 0, -minIndex); - return gl::Error(GL_NO_ERROR); - } + mDeviceContext->DrawIndexed(count, 0, -minIndex); } + return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer) +gl::Error Renderer11::drawLineLoop(const gl::Data &data, + GLsizei count, + GLenum type, + const GLvoid *indexPointer, + const TranslatedIndexData *indexInfo, + int instances) { + gl::VertexArray *vao = data.state->getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + + const GLvoid *indices = indexPointer; + // Get the raw indices for an indexed draw if (type != GL_NONE && elementArrayBuffer) { @@ -1831,7 +2075,11 @@ gl::Error Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *ind return gl::Error(GL_OUT_OF_MEMORY, "Failed to create a 32-bit looping index buffer for GL_LINE_LOOP, too many indices required."); } - const unsigned int spaceNeeded = (static_cast(count) + 1) * sizeof(unsigned int); + GetLineLoopIndices(indices, type, static_cast(count), + data.state->isPrimitiveRestartEnabled(), &mScratchIndexDataBuffer); + + unsigned int spaceNeeded = + static_cast(sizeof(GLuint) * mScratchIndexDataBuffer.size()); gl::Error error = mLineLoopIB->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT); if (error.isError()) { @@ -1846,41 +2094,9 @@ gl::Error Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *ind return error; } - unsigned int *data = reinterpret_cast(mappedMemory); - unsigned int indexBufferOffset = offset; - - switch (type) - { - case GL_NONE: // Non-indexed draw - for (int i = 0; i < count; i++) - { - data[i] = i; - } - data[count] = 0; - break; - case GL_UNSIGNED_BYTE: - for (int i = 0; i < count; i++) - { - data[i] = static_cast(indices)[i]; - } - data[count] = static_cast(indices)[0]; - break; - case GL_UNSIGNED_SHORT: - for (int i = 0; i < count; i++) - { - data[i] = static_cast(indices)[i]; - } - data[count] = static_cast(indices)[0]; - break; - case GL_UNSIGNED_INT: - for (int i = 0; i < count; i++) - { - data[i] = static_cast(indices)[i]; - } - data[count] = static_cast(indices)[0]; - break; - default: UNREACHABLE(); - } + // Copy over the converted index data. + memcpy(mappedMemory, &mScratchIndexDataBuffer[0], + sizeof(GLuint) * mScratchIndexDataBuffer.size()); error = mLineLoopIB->unmapBuffer(); if (error.isError()) @@ -1892,21 +2108,42 @@ gl::Error Renderer11::drawLineLoop(GLsizei count, GLenum type, const GLvoid *ind ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); - if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset) + if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || + mAppliedIBOffset != offset) { - mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, indexBufferOffset); + mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, offset); mAppliedIB = d3dIndexBuffer; mAppliedIBFormat = indexFormat; - mAppliedIBOffset = indexBufferOffset; + mAppliedIBOffset = offset; } - mDeviceContext->DrawIndexed(count + 1, 0, -minIndex); + INT baseVertexLocation = (indexInfo ? -static_cast(indexInfo->indexRange.start) : 0); + UINT indexCount = static_cast(mScratchIndexDataBuffer.size()); + + if (instances > 0) + { + mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, baseVertexLocation, 0); + } + else + { + mDeviceContext->DrawIndexed(indexCount, 0, baseVertexLocation); + } return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer, int instances) +gl::Error Renderer11::drawTriangleFan(const gl::Data &data, + GLsizei count, + GLenum type, + const GLvoid *indices, + int minIndex, + int instances) { + gl::VertexArray *vao = data.state->getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + + const GLvoid *indexPointer = indices; + // Get the raw indices for an indexed draw if (type != GL_NONE && elementArrayBuffer) { @@ -1920,7 +2157,7 @@ gl::Error Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid * return error; } - indices = bufferData + offset; + indexPointer = bufferData + offset; } if (!mTriangleFanIB) @@ -1937,21 +2174,25 @@ gl::Error Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid * // Checked by Renderer11::applyPrimitiveType ASSERT(count >= 3); - const unsigned int numTris = count - 2; + const GLuint numTris = count - 2; if (numTris > (std::numeric_limits::max() / (sizeof(unsigned int) * 3))) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, too many indices required."); } - const unsigned int spaceNeeded = (numTris * 3) * sizeof(unsigned int); + GetTriFanIndices(indexPointer, type, count, data.state->isPrimitiveRestartEnabled(), + &mScratchIndexDataBuffer); + + const unsigned int spaceNeeded = + static_cast(mScratchIndexDataBuffer.size() * sizeof(unsigned int)); gl::Error error = mTriangleFanIB->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT); if (error.isError()) { return error; } - void* mappedMemory = NULL; + void *mappedMemory = nullptr; unsigned int offset; error = mTriangleFanIB->mapBuffer(spaceNeeded, &mappedMemory, &offset); if (error.isError()) @@ -1959,45 +2200,7 @@ gl::Error Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid * return error; } - unsigned int *data = reinterpret_cast(mappedMemory); - unsigned int indexBufferOffset = offset; - - switch (type) - { - case GL_NONE: // Non-indexed draw - for (unsigned int i = 0; i < numTris; i++) - { - data[i*3 + 0] = 0; - data[i*3 + 1] = i + 1; - data[i*3 + 2] = i + 2; - } - break; - case GL_UNSIGNED_BYTE: - for (unsigned int i = 0; i < numTris; i++) - { - data[i*3 + 0] = static_cast(indices)[0]; - data[i*3 + 1] = static_cast(indices)[i + 1]; - data[i*3 + 2] = static_cast(indices)[i + 2]; - } - break; - case GL_UNSIGNED_SHORT: - for (unsigned int i = 0; i < numTris; i++) - { - data[i*3 + 0] = static_cast(indices)[0]; - data[i*3 + 1] = static_cast(indices)[i + 1]; - data[i*3 + 2] = static_cast(indices)[i + 2]; - } - break; - case GL_UNSIGNED_INT: - for (unsigned int i = 0; i < numTris; i++) - { - data[i*3 + 0] = static_cast(indices)[0]; - data[i*3 + 1] = static_cast(indices)[i + 1]; - data[i*3 + 2] = static_cast(indices)[i + 2]; - } - break; - default: UNREACHABLE(); - } + memcpy(mappedMemory, &mScratchIndexDataBuffer[0], spaceNeeded); error = mTriangleFanIB->unmapBuffer(); if (error.isError()) @@ -2009,32 +2212,32 @@ gl::Error Renderer11::drawTriangleFan(GLsizei count, GLenum type, const GLvoid * ID3D11Buffer *d3dIndexBuffer = indexBuffer->getBuffer(); DXGI_FORMAT indexFormat = indexBuffer->getIndexFormat(); - if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset) + if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || + mAppliedIBOffset != offset) { - mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, indexBufferOffset); + mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, offset); mAppliedIB = d3dIndexBuffer; mAppliedIBFormat = indexFormat; - mAppliedIBOffset = indexBufferOffset; + mAppliedIBOffset = offset; } + UINT indexCount = static_cast(mScratchIndexDataBuffer.size()); + if (instances > 0) { - mDeviceContext->DrawIndexedInstanced(numTris * 3, instances, 0, -minIndex, 0); + mDeviceContext->DrawIndexedInstanced(indexCount, instances, 0, -minIndex, 0); } else { - mDeviceContext->DrawIndexed(numTris * 3, 0, -minIndex); + mDeviceContext->DrawIndexed(indexCount, 0, -minIndex); } return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::applyShaders(gl::Program *program, - const gl::Framebuffer *framebuffer, - bool rasterizerDiscard, - bool transformFeedbackActive) +gl::Error Renderer11::applyShadersImpl(const gl::Data &data, GLenum drawMode) { - ProgramD3D *programD3D = GetImplAs(program); + ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); const auto &inputLayout = programD3D->getCachedInputLayout(); ShaderExecutableD3D *vertexExe = NULL; @@ -2044,30 +2247,39 @@ gl::Error Renderer11::applyShaders(gl::Program *program, return error; } + const gl::Framebuffer *drawFramebuffer = data.state->getDrawFramebuffer(); ShaderExecutableD3D *pixelExe = NULL; - error = programD3D->getPixelExecutableForFramebuffer(framebuffer, &pixelExe); + error = programD3D->getPixelExecutableForFramebuffer(drawFramebuffer, &pixelExe); if (error.isError()) { return error; } - ShaderExecutableD3D *geometryExe = programD3D->getGeometryExecutable(); + ShaderExecutableD3D *geometryExe = nullptr; + error = + programD3D->getGeometryExecutableForPrimitiveType(data, drawMode, &geometryExe, nullptr); + if (error.isError()) + { + return error; + } ID3D11VertexShader *vertexShader = (vertexExe ? GetAs(vertexExe)->getVertexShader() : NULL); ID3D11PixelShader *pixelShader = NULL; // Skip pixel shader if we're doing rasterizer discard. + bool rasterizerDiscard = data.state->getRasterizerState().rasterizerDiscard; if (!rasterizerDiscard) { pixelShader = (pixelExe ? GetAs(pixelExe)->getPixelShader() : NULL); } ID3D11GeometryShader *geometryShader = NULL; + bool transformFeedbackActive = data.state->isTransformFeedbackActiveUnpaused(); if (transformFeedbackActive) { geometryShader = (vertexExe ? GetAs(vertexExe)->getStreamOutShader() : NULL); } - else if (mCurRasterState.pointDrawMode) + else { geometryShader = (geometryExe ? GetAs(geometryExe)->getGeometryShader() : NULL); } @@ -2104,6 +2316,7 @@ gl::Error Renderer11::applyShaders(gl::Program *program, } gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, + GLenum drawMode, const std::vector &uniformArray) { unsigned int totalRegisterCountVS = 0; @@ -2263,7 +2476,7 @@ gl::Error Renderer11::applyUniforms(const ProgramD3D &programD3D, } // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it if necessary - if (programD3D.usesGeometryShader()) + if (programD3D.usesGeometryShader(drawMode)) { // needed for the point sprite geometry shader if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS) @@ -2310,7 +2523,7 @@ void Renderer11::markAllStateDirty() mForceSetPixelSamplerStates[fsamplerId] = true; } - mForceSetBlendState = true; + mStateManager.forceSetBlendState(); mForceSetRasterState = true; mForceSetDepthStencilState = true; mForceSetScissor = true; @@ -3044,9 +3257,12 @@ ProgramImpl *Renderer11::createProgram(const gl::Program::Data &data) return new ProgramD3D(data, this); } -gl::Error Renderer11::loadExecutable(const void *function, size_t length, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, ShaderExecutableD3D **outExecutable) +gl::Error Renderer11::loadExecutable(const void *function, + size_t length, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) { switch (type) { @@ -3062,25 +3278,22 @@ gl::Error Renderer11::loadExecutable(const void *function, size_t length, Shader return gl::Error(GL_OUT_OF_MEMORY, "Failed to create vertex shader, result: 0x%X.", result); } - if (transformFeedbackVaryings.size() > 0) + if (!streamOutVaryings.empty()) { std::vector soDeclaration; - for (size_t i = 0; i < transformFeedbackVaryings.size(); i++) - { - const gl::LinkedVarying &varying = transformFeedbackVaryings[i]; - GLenum transposedType = gl::TransposeMatrixType(varying.type); + soDeclaration.reserve(streamOutVaryings.size()); - for (unsigned int j = 0; j < varying.semanticIndexCount; j++) - { - D3D11_SO_DECLARATION_ENTRY entry = { 0 }; - entry.Stream = 0; - entry.SemanticName = varying.semanticName.c_str(); - entry.SemanticIndex = varying.semanticIndex + j; - entry.StartComponent = 0; - entry.ComponentCount = static_cast(gl::VariableColumnCount(transposedType)); - entry.OutputSlot = static_cast((separatedOutputBuffers ? i : 0)); - soDeclaration.push_back(entry); - } + for (const auto &streamOutVarying : streamOutVaryings) + { + D3D11_SO_DECLARATION_ENTRY entry = {0}; + entry.Stream = 0; + entry.SemanticName = streamOutVarying.semanticName.c_str(); + entry.SemanticIndex = streamOutVarying.semanticIndex; + entry.StartComponent = 0; + entry.ComponentCount = static_cast(streamOutVarying.componentCount); + entry.OutputSlot = static_cast( + (separatedOutputBuffers ? streamOutVarying.outputSlot : 0)); + soDeclaration.push_back(entry); } result = mDevice->CreateGeometryShaderWithStreamOutput( @@ -3133,9 +3346,12 @@ gl::Error Renderer11::loadExecutable(const void *function, size_t length, Shader return gl::Error(GL_NO_ERROR); } -gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, const std::string &shaderHLSL, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, const D3DCompilerWorkarounds &workarounds, +gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, + const std::string &shaderHLSL, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + const D3DCompilerWorkarounds &workarounds, ShaderExecutableD3D **outExectuable) { const char *profileType = NULL; @@ -3178,6 +3394,15 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, const std::strin configs.push_back(CompileConfig(flags | D3DCOMPILE_SKIP_VALIDATION, "skip validation" )); configs.push_back(CompileConfig(flags | D3DCOMPILE_SKIP_OPTIMIZATION, "skip optimization")); + if (getMajorShaderModel() == 4 && getShaderModelSuffix() != "") + { + // Some shaders might cause a "blob content mismatch between level9 and d3d10 shader". + // e.g. dEQP-GLES2.functional.shaders.struct.local.loop_nested_struct_array_*. + // Using the [unroll] directive works around this, as does this D3DCompile flag. + configs.push_back( + CompileConfig(flags | D3DCOMPILE_AVOID_FLOW_CONTROL, "avoid flow control")); + } + D3D_SHADER_MACRO loopMacros[] = { {"ANGLE_ENABLE_LOOP_FLATTEN", "1"}, {0, 0} }; ID3DBlob *binary = NULL; @@ -3197,7 +3422,7 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, const std::strin } error = loadExecutable(binary->GetBufferPointer(), binary->GetBufferSize(), type, - transformFeedbackVaryings, separatedOutputBuffers, outExectuable); + streamOutVaryings, separatedOutputBuffers, outExectuable); SafeRelease(binary); if (error.isError()) @@ -3945,4 +4170,14 @@ gl::Error Renderer11::clearTextures(gl::SamplerType samplerType, size_t rangeSta return gl::Error(GL_NO_ERROR); } +egl::Error Renderer11::initializeEGLDevice(DeviceD3D **outDevice) +{ + if (*outDevice == nullptr) + { + ASSERT(mDevice != nullptr); + *outDevice = new DeviceD3D(reinterpret_cast(mDevice), EGL_D3D11_DEVICE_ANGLE); + } + + return egl::Error(EGL_SUCCESS); +} } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h index ede706e03640..ee4543fa69da 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h @@ -19,6 +19,7 @@ #include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h" #include "libANGLE/renderer/d3d/d3d11/InputLayoutCache.h" #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" +#include "libANGLE/renderer/d3d/d3d11/StateManager11.h" namespace gl { @@ -121,8 +122,11 @@ class Renderer11 : public RendererD3D const std::vector &fragmentUniformBuffers) override; virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState); - gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, + gl::Error setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, unsigned int sampleMask) override; + virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, int stencilBackRef, bool frontFaceCCW); @@ -132,15 +136,17 @@ class Renderer11 : public RendererD3D virtual bool applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize); gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override; - gl::Error applyShaders(gl::Program *program, - const gl::Framebuffer *framebuffer, - bool rasterizerDiscard, - bool transformFeedbackActive) override; - gl::Error applyUniforms(const ProgramD3D &programD3D, + GLenum drawMode, const std::vector &uniformArray) override; virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo); - virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo); + gl::Error applyIndexBuffer(const gl::Data &data, + const GLvoid *indices, + GLsizei count, + GLenum mode, + GLenum type, + TranslatedIndexData *indexInfo, + SourceIndexData *sourceIndexInfo) override; void applyTransformFeedbackBuffers(const gl::State &state) override; virtual void markAllStateDirty(); @@ -187,14 +193,20 @@ class Renderer11 : public RendererD3D ProgramImpl *createProgram(const gl::Program::Data &data) override; // Shader operations - virtual gl::Error loadExecutable(const void *function, size_t length, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, ShaderExecutableD3D **outExecutable); - virtual gl::Error compileToExecutable(gl::InfoLog &infoLog, const std::string &shaderHLSL, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, const D3DCompilerWorkarounds &workarounds, - ShaderExecutableD3D **outExectuable); - virtual UniformStorageD3D *createUniformStorage(size_t storageSize); + gl::Error loadExecutable(const void *function, + size_t length, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) override; + gl::Error compileToExecutable(gl::InfoLog &infoLog, + const std::string &shaderHLSL, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + const D3DCompilerWorkarounds &workarounds, + ShaderExecutableD3D **outExectuable) override; + UniformStorageD3D *createUniformStorage(size_t storageSize) override; // Image operations virtual ImageD3D *createImage(); @@ -237,6 +249,8 @@ class Renderer11 : public RendererD3D ID3D11DeviceContext1 *getDeviceContext1IfSupported() { return mDeviceContext1; }; DXGIFactory *getDxgiFactory() { return mDxgiFactory; }; + RenderStateCache &getStateCache() { return mStateCache; } + Blit11 *getBlitter() { return mBlit; } Clear11 *getClearer() { return mClear; } @@ -274,21 +288,24 @@ class Renderer11 : public RendererD3D protected: void createAnnotator() override; gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) override; + gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) override; + + egl::Error initializeEGLDevice(DeviceD3D **outDevice) override; + + void syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) override; private: gl::Error drawArraysImpl(const gl::Data &data, GLenum mode, GLsizei count, - GLsizei instances, - bool usesPointSize) override; - gl::Error drawElementsImpl(GLenum mode, + GLsizei instances) override; + gl::Error drawElementsImpl(const gl::Data &data, + const TranslatedIndexData &indexInfo, + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, - gl::Buffer *elementArrayBuffer, - const TranslatedIndexData &indexInfo, - GLsizei instances, - bool usesPointSize) override; + GLsizei instances) override; void generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, gl::Extensions *outExtensions, @@ -296,8 +313,18 @@ class Renderer11 : public RendererD3D WorkaroundsD3D generateWorkarounds() const override; - gl::Error drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer); - gl::Error drawTriangleFan(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer, int instances); + gl::Error drawLineLoop(const gl::Data &data, + GLsizei count, + GLenum type, + const GLvoid *indices, + const TranslatedIndexData *indexInfo, + int instances); + gl::Error drawTriangleFan(const gl::Data &data, + GLsizei count, + GLenum type, + const GLvoid *indices, + int minIndex, + int instances); ID3D11Texture2D *resolveMultisampledTexture(ID3D11Texture2D *source, unsigned int subresource); void unsetConflictingSRVs(gl::SamplerType shaderType, uintptr_t resource, const gl::ImageIndex &index); @@ -324,6 +351,7 @@ class Renderer11 : public RendererD3D uintptr_t mAppliedDSV; bool mDepthStencilInitialized; bool mRenderTargetDescInitialized; + unsigned int mCurStencilSize; struct RenderTargetDesc { @@ -383,11 +411,7 @@ class Renderer11 : public RendererD3D // A block of NULL pointers, cached so we don't re-allocate every draw call std::vector mNullSRVs; - // Currently applied blend state - bool mForceSetBlendState; - gl::BlendState mCurBlendState; - gl::ColorF mCurBlendColor; - unsigned int mCurSampleMask; + StateManager11 mStateManager; // Currently applied rasterizer state bool mForceSetRasterState; @@ -488,6 +512,8 @@ class Renderer11 : public RendererD3D char mDescription[128]; DXGIFactory *mDxgiFactory; ID3D11Debug *mDebug; + + std::vector mScratchIndexDataBuffer; }; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp new file mode 100644 index 000000000000..b52073129e3a --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp @@ -0,0 +1,161 @@ +// +// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// StateManager11.cpp: Defines a class for caching D3D11 state + +#include "libANGLE/renderer/d3d/d3d11/StateManager11.h" + +#include "common/BitSetIterator.h" +#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" + +namespace rx +{ + +StateManager11::StateManager11(Renderer11 *renderer11) + : mBlendStateIsDirty(false), + mCurBlendColor(0, 0, 0, 0), + mCurSampleMask(0), + mRenderer11(renderer11) +{ + mCurBlendState.blend = false; + mCurBlendState.sourceBlendRGB = GL_ONE; + mCurBlendState.destBlendRGB = GL_ZERO; + mCurBlendState.sourceBlendAlpha = GL_ONE; + mCurBlendState.destBlendAlpha = GL_ZERO; + mCurBlendState.blendEquationRGB = GL_FUNC_ADD; + mCurBlendState.blendEquationAlpha = GL_FUNC_ADD; + mCurBlendState.colorMaskRed = true; + mCurBlendState.colorMaskBlue = true; + mCurBlendState.colorMaskGreen = true; + mCurBlendState.colorMaskAlpha = true; + mCurBlendState.sampleAlphaToCoverage = false; + mCurBlendState.dither = false; +} + +StateManager11::~StateManager11() +{ +} + +void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) +{ + for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits)) + { + switch (dirtyBit) + { + case gl::State::DIRTY_BIT_BLEND_EQUATIONS: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.blendEquationRGB != mCurBlendState.blendEquationRGB || + blendState.blendEquationAlpha != mCurBlendState.blendEquationAlpha) + { + mBlendStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_BLEND_FUNCS: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.sourceBlendRGB != mCurBlendState.sourceBlendRGB || + blendState.destBlendRGB != mCurBlendState.destBlendRGB || + blendState.sourceBlendAlpha != mCurBlendState.sourceBlendAlpha || + blendState.destBlendAlpha != mCurBlendState.destBlendAlpha) + { + mBlendStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_BLEND_ENABLED: + if (state.getBlendState().blend != mCurBlendState.blend) + { + mBlendStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: + if (state.getBlendState().sampleAlphaToCoverage != + mCurBlendState.sampleAlphaToCoverage) + { + mBlendStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_DITHER_ENABLED: + if (state.getBlendState().dither != mCurBlendState.dither) + { + mBlendStateIsDirty = true; + } + break; + case gl::State::DIRTY_BIT_COLOR_MASK: + { + const gl::BlendState &blendState = state.getBlendState(); + if (blendState.colorMaskRed != mCurBlendState.colorMaskRed || + blendState.colorMaskGreen != mCurBlendState.colorMaskGreen || + blendState.colorMaskBlue != mCurBlendState.colorMaskBlue || + blendState.colorMaskAlpha != mCurBlendState.colorMaskAlpha) + { + mBlendStateIsDirty = true; + } + break; + } + case gl::State::DIRTY_BIT_BLEND_COLOR: + if (state.getBlendColor() != mCurBlendColor) + { + mBlendStateIsDirty = true; + } + break; + default: + break; + } + } +} + +gl::Error StateManager11::setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, + unsigned int sampleMask) +{ + if (mBlendStateIsDirty || sampleMask != mCurSampleMask) + { + ID3D11BlendState *dxBlendState = nullptr; + gl::Error error = + mRenderer11->getStateCache().getBlendState(framebuffer, blendState, &dxBlendState); + if (error.isError()) + { + return error; + } + + ASSERT(dxBlendState != nullptr); + + float blendColors[4] = {0.0f}; + if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA && + blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_CONSTANT_ALPHA && + blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA) + { + blendColors[0] = blendColor.red; + blendColors[1] = blendColor.green; + blendColors[2] = blendColor.blue; + blendColors[3] = blendColor.alpha; + } + else + { + blendColors[0] = blendColor.alpha; + blendColors[1] = blendColor.alpha; + blendColors[2] = blendColor.alpha; + blendColors[3] = blendColor.alpha; + } + + mRenderer11->getDeviceContext()->OMSetBlendState(dxBlendState, blendColors, sampleMask); + + mCurBlendState = blendState; + mCurBlendColor = blendColor; + mCurSampleMask = sampleMask; + + mBlendStateIsDirty = false; + } + + return gl::Error(GL_NO_ERROR); +} + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h new file mode 100644 index 000000000000..673897261659 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h @@ -0,0 +1,51 @@ +// +// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// StateManager11.h: Defines a class for caching D3D11 state + +#ifndef LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ +#define LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ + +#include "libANGLE/angletypes.h" +#include "libANGLE/Data.h" +#include "libANGLE/State.h" +#include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h" + +namespace rx +{ + +class Renderer11; + +class StateManager11 final : angle::NonCopyable +{ + public: + StateManager11(Renderer11 *renderer11); + + ~StateManager11(); + + void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits); + + gl::Error setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, + unsigned int sampleMask); + + void forceSetBlendState() { mBlendStateIsDirty = true; } + + private: + // Blend State + bool mBlendStateIsDirty; + // TODO(dianx) temporary representation of a dirty bit. once we move enough states in, + // try experimenting with dirty bit instead of a bool + gl::BlendState mCurBlendState; + gl::ColorF mCurBlendColor; + unsigned int mCurSampleMask; + + Renderer11 *mRenderer11; +}; + +} // namespace rx +#endif // LIBANGLE_RENDERER_D3D11_STATEMANAGER11_H_ \ No newline at end of file diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py new file mode 100644 index 000000000000..0985ba8c6870 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_load_functions_table.py @@ -0,0 +1,199 @@ +#!/usr/bin/python +# Copyright 2015 The ANGLE Project Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# gen_load_functions_table.py: +# Code generation for the load function tables used for texture formats +# + +import json + +template = """// GENERATED FILE - DO NOT EDIT. +// Generated by gen_load_functions_table.py using data from load_functions_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// load_functions_table: +// Contains the GetLoadFunctionsMap for texture_format_util.h +// + +#include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" +#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/loadimage.h" +#include "libANGLE/renderer/d3d/loadimage_etc.h" + +namespace rx +{{ + +namespace d3d11 +{{ + +namespace +{{ + +// ES3 image loading functions vary based on: +// - the GL internal format (supplied to glTex*Image*D) +// - the GL data type given (supplied to glTex*Image*D) +// - the target DXGI_FORMAT that the image will be loaded into (which is chosen based on the D3D +// device's capabilities) +// This map type determines which loading function to use, based on these three parameters. +// Source formats and types are taken from Tables 3.2 and 3.3 of the ES 3 spec. +void UnimplementedLoadFunction(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{{ + UNIMPLEMENTED(); +}} + +void UnreachableLoadFunction(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{{ + UNREACHABLE(); +}} + +}} // namespace + +// TODO we can replace these maps with more generated code +const std::map &GetLoadFunctionsMap(GLenum {internal_format}, + DXGI_FORMAT {dxgi_format}) +{{ + // clang-format off + switch ({internal_format}) + {{ +{data} + default: + {{ + static std::map emptyLoadFunctionsMap; + return emptyLoadFunctionsMap; + }} + }} + // clang-format on + +}} // GetLoadFunctionsMap + +}} // namespace d3d11 + +}} // namespace rx +""" + +internal_format_param = 'internalFormat' +dxgi_format_param = 'dxgiFormat' +dxgi_format_unknown = "DXGI_FORMAT_UNKNOWN" + +def get_function_maps_string(typestr, function): + return ' loadMap[' + typestr + '] = ' + function + ';\n' + +def get_unknown_format_string(dxgi_to_type_map, dxgi_unknown_string): + if dxgi_unknown_string not in dxgi_to_type_map: + return '' + + table_data = '' + + for unknown_type_function in dxgi_to_type_map[dxgi_unknown_string]: + table_data += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction']) + + return table_data + +# Making map from dxgi to type map for a particular internalFormat +def create_dxgi_to_type_map(dst, json_data, internal_format_str): + for type_item in sorted(json_data[internal_format_str].iteritems()): + for entry_in_type_item in type_item[1]: + dxgi_format_str = entry_in_type_item['dxgiFormat'] + + if dxgi_format_str not in dst: + dst[dxgi_format_str] = [] + + type_dxgi_load_function = entry_in_type_item.copy(); + type_dxgi_load_function['type'] = type_item[0] + dst[dxgi_format_str].append(type_dxgi_load_function) + +def get_load_function_map_snippet(insert_map_string): + load_function_map_snippet = '' + load_function_map_snippet += ' static const std::map loadFunctionsMap = []() {\n' + load_function_map_snippet += ' std::map loadMap;\n' + load_function_map_snippet += insert_map_string + load_function_map_snippet += ' return loadMap;\n' + load_function_map_snippet += ' }();\n\n' + load_function_map_snippet += ' return loadFunctionsMap;\n' + + return load_function_map_snippet + +def parse_json_into_switch_string(json_data): + table_data = '' + for internal_format_item in sorted(json_data.iteritems()): + internal_format_str = internal_format_item[0] + table_data += ' case ' + internal_format_str + ':\n' + table_data += ' {\n' + table_data += ' switch (' + dxgi_format_param + ')\n' + table_data += ' {\n' + + dxgi_to_type_map = {}; + create_dxgi_to_type_map(dxgi_to_type_map, json_data, internal_format_str) + + dxgi_unknown_str = get_unknown_format_string(dxgi_to_type_map, dxgi_format_unknown); + + for dxgi_format_item in sorted(dxgi_to_type_map.iteritems()): + dxgi_format_str = dxgi_format_item[0] + + # Main case statements + table_data += ' case ' + dxgi_format_str + ':\n' + table_data += ' {\n' + insert_map_string = '' + types_already_in_loadmap = set() + for type_function in sorted(dxgi_format_item[1]): + # type_function['requiresConversion'] element is not in use at the moment but may be needed later + insert_map_string += get_function_maps_string(type_function['type'], type_function['loadFunction']) + types_already_in_loadmap.add(type_function['type']) + + # DXGI_FORMAT_UNKNOWN add ons + if dxgi_format_unknown in dxgi_to_type_map: + for unknown_type_function in dxgi_to_type_map[dxgi_format_unknown]: + # Check that it's not already in the loadmap so it doesn't override the value + if unknown_type_function['type'] not in types_already_in_loadmap: + insert_map_string += get_function_maps_string(unknown_type_function['type'], unknown_type_function['loadFunction']) + + table_data += get_load_function_map_snippet(insert_map_string) + table_data += ' }\n' + + table_data += ' default:\n' + + if dxgi_unknown_str: + table_data += ' {\n' + table_data += get_load_function_map_snippet(dxgi_unknown_str) + table_data += ' }\n' + else: + table_data += ' break;\n' + table_data += ' }\n' + table_data += ' }\n' + + return table_data + +with open('load_functions_data.json') as functions_json_file: + functions_data = functions_json_file.read(); + functions_json_file.close() + json_data = json.loads(functions_data) + + table_data = parse_json_into_switch_string(json_data) + output = template.format(internal_format = internal_format_param, + dxgi_format = dxgi_format_param, + data=table_data) + + with open('load_functions_table_autogen.cpp', 'wt') as out_file: + out_file.write(output) + out_file.close() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_swizzle_format_table.py b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_swizzle_format_table.py new file mode 100644 index 000000000000..212b19edd165 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_swizzle_format_table.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# Copyright 2015 The ANGLE Project Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# gen_swizzle_format_table.py: +# Code generation for the swizzle format table used for texture formats +# + +import json +import pprint + +template = """// GENERATED FILE - DO NOT EDIT +// Generated by gen_swizzle_format_table.py using data from swizzle_format_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// swizzle_format_info: +// Provides information for swizzle format and a map from type->formatinfo +// + +#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" + +#include + +namespace rx +{{ + +namespace d3d11 +{{ + +SwizzleSizeType::SwizzleSizeType() : maxComponentSize(0), componentType(GL_NONE) +{{ +}} + +SwizzleSizeType::SwizzleSizeType(size_t maxComponentSize, GLenum componentType) + : maxComponentSize(maxComponentSize), componentType(componentType) +{{ +}} + +bool SwizzleSizeType::operator<(const SwizzleSizeType &other) const +{{ + return (maxComponentSize != other.maxComponentSize) + ? (maxComponentSize < other.maxComponentSize) + : (componentType < other.componentType); +}} + +SwizzleFormatInfo::SwizzleFormatInfo() + : mTexFormat(DXGI_FORMAT_UNKNOWN), + mSRVFormat(DXGI_FORMAT_UNKNOWN), + mRTVFormat(DXGI_FORMAT_UNKNOWN) +{{ +}} + +SwizzleFormatInfo::SwizzleFormatInfo(DXGI_FORMAT texFormat, + DXGI_FORMAT srvFormat, + DXGI_FORMAT rtvFormat) + : mTexFormat(texFormat), mSRVFormat(srvFormat), mRTVFormat(rtvFormat) +{{ +}} + +const SwizzleFormatInfo &GetSwizzleFormatInfo(GLuint maxBits, GLenum componentType) +{{ + // clang-format off + switch ({component_type_param}) + {{ +{data} + default: + {{ + static const SwizzleFormatInfo defaultInfo(DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return defaultInfo; + }} + }} + // clang-format on + +}} // GetSwizzleFormatInfo + +}} // namespace d3d11 + +}} // namespace rx +""" + +max_bits_param = 'maxBits' +component_type_param = 'componentType' + +tex_format_key = 'texFormat' +srv_format_key = 'srvFormat' +rtv_format_key = 'rtvFormat' + + +def parse_json_into_switch_string(json_data): + table_data = '' + for component_type in sorted(json_data.iteritems()): + type_str = component_type[0] + table_data += ' case ' + type_str + ':\n' + table_data += ' {\n' + + table_data += ' switch (' + max_bits_param + ')\n' + table_data += ' {\n' + + for max_width_item in sorted(json_data[type_str].iteritems()): + max_width = max_width_item[0] + table_data += ' case ' + max_width + ':\n' + table_data += ' {\n' + table_data += ' static const SwizzleFormatInfo formatInfo(' + json_data[type_str][max_width][tex_format_key] + ',\n' + table_data += ' ' + json_data[type_str][max_width][srv_format_key] + ',\n' + table_data += ' ' + json_data[type_str][max_width][rtv_format_key] + ');\n' + table_data += ' return formatInfo;\n' + + table_data += ' }\n' + table_data += ' default:\n' + table_data += ' break;\n' + table_data += ' }\n' + table_data += ' }\n' + + return table_data + +with open('swizzle_format_data.json') as functions_json_file: + functions_data = functions_json_file.read(); + functions_json_file.close() + json_data = json.loads(functions_data) + + table_data = parse_json_into_switch_string(json_data) + output = template.format(component_type_param=component_type_param, + data=table_data) + + with open('swizzle_format_info_autogen.cpp', 'wt') as out_file: + out_file.write(output) + out_file.close() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py new file mode 100644 index 000000000000..b065466df6d6 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# Copyright 2015 The ANGLE Project Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# gen_texture_format_table.py: +# Code generation for texture format map +# + +import json +import pprint + +template = """// GENERATED FILE - DO NOT EDIT. +// Generated by gen_texture_format_table.py using data from texture_format_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// texture_format_table: +// Queries for full textureFormat information based in internalFormat +// + +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" + +#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h" +#include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" +#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" +#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" +#include "libANGLE/renderer/d3d/loadimage.h" + +namespace rx +{{ + +namespace d3d11 +{{ + +namespace +{{ + +typedef bool (*FormatSupportFunction)(const Renderer11DeviceCaps &); + +bool AnyDevice(const Renderer11DeviceCaps &deviceCaps) +{{ + return true; +}} + +bool OnlyFL10Plus(const Renderer11DeviceCaps &deviceCaps) +{{ + return (deviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0); +}} + +bool OnlyFL9_3(const Renderer11DeviceCaps &deviceCaps) +{{ + return (deviceCaps.featureLevel == D3D_FEATURE_LEVEL_9_3); +}} + +template +bool SupportsFormat(const Renderer11DeviceCaps &deviceCaps) +{{ + // Must support texture, SRV and RTV support + UINT mustSupport = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_TEXTURECUBE | + D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_MIP | + D3D11_FORMAT_SUPPORT_RENDER_TARGET; + + if (d3d11_gl::GetMaximumClientVersion(deviceCaps.featureLevel) > 2) + {{ + mustSupport |= D3D11_FORMAT_SUPPORT_TEXTURE3D; + }} + + bool fullSupport = false; + if (format == DXGI_FORMAT_B5G6R5_UNORM) + {{ + // All hardware that supports DXGI_FORMAT_B5G6R5_UNORM should support autogen mipmaps, but + // check anyway. + mustSupport |= D3D11_FORMAT_SUPPORT_MIP_AUTOGEN; + fullSupport = ((deviceCaps.B5G6R5support & mustSupport) == mustSupport); + }} + else if (format == DXGI_FORMAT_B4G4R4A4_UNORM) + {{ + fullSupport = ((deviceCaps.B4G4R4A4support & mustSupport) == mustSupport); + }} + else if (format == DXGI_FORMAT_B5G5R5A1_UNORM) + {{ + fullSupport = ((deviceCaps.B5G5R5A1support & mustSupport) == mustSupport); + }} + else + {{ + UNREACHABLE(); + return false; + }} + + // This 'SupportsFormat' function is used by individual entries in the D3D11 Format Map below, + // which maps GL formats to DXGI formats. + if (requireSupport) + {{ + // This means that ANGLE would like to use the entry in the map if the inputted DXGI format + // *IS* supported. + // e.g. the entry might map GL_RGB5_A1 to DXGI_FORMAT_B5G5R5A1, which should only be used if + // DXGI_FORMAT_B5G5R5A1 is supported. + // In this case, we should only return 'true' if the format *IS* supported. + return fullSupport; + }} + else + {{ + // This means that ANGLE would like to use the entry in the map if the inputted DXGI format + // *ISN'T* supported. + // This might be a fallback entry. e.g. for ANGLE to use DXGI_FORMAT_R8G8B8A8_UNORM if + // DXGI_FORMAT_B5G5R5A1 isn't supported. + // In this case, we should only return 'true' if the format *ISN'T* supported. + return !fullSupport; + }} +}} + +// End Format Support Functions + +// For sized GL internal formats, there are several possible corresponding D3D11 formats depending +// on device capabilities. +// This function allows querying for the DXGI texture formats to use for textures, SRVs, RTVs and +// DSVs given a GL internal format. +const TextureFormat GetD3D11FormatInfo(GLenum internalFormat, + DXGI_FORMAT texFormat, + DXGI_FORMAT srvFormat, + DXGI_FORMAT rtvFormat, + DXGI_FORMAT dsvFormat) +{{ + TextureFormat info; + info.texFormat = texFormat; + info.srvFormat = srvFormat; + info.rtvFormat = rtvFormat; + info.dsvFormat = dsvFormat; + + // Given a GL internal format, the renderFormat is the DSV format if it is depth- or + // stencil-renderable, + // the RTV format if it is color-renderable, and the (nonrenderable) texture format otherwise. + if (dsvFormat != DXGI_FORMAT_UNKNOWN) + {{ + info.renderFormat = dsvFormat; + }} + else if (rtvFormat != DXGI_FORMAT_UNKNOWN) + {{ + info.renderFormat = rtvFormat; + }} + else if (texFormat != DXGI_FORMAT_UNKNOWN) + {{ + info.renderFormat = texFormat; + }} + else + {{ + info.renderFormat = DXGI_FORMAT_UNKNOWN; + }} + + // Compute the swizzle formats + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); + if (internalFormat != GL_NONE && formatInfo.pixelBytes > 0) + {{ + if (formatInfo.componentCount != 4 || texFormat == DXGI_FORMAT_UNKNOWN || + srvFormat == DXGI_FORMAT_UNKNOWN || rtvFormat == DXGI_FORMAT_UNKNOWN) + {{ + // Get the maximum sized component + unsigned int maxBits = 1; + if (formatInfo.compressed) + {{ + unsigned int compressedBitsPerBlock = formatInfo.pixelBytes * 8; + unsigned int blockSize = + formatInfo.compressedBlockWidth * formatInfo.compressedBlockHeight; + maxBits = std::max(compressedBitsPerBlock / blockSize, maxBits); + }} + else + {{ + maxBits = std::max(maxBits, formatInfo.alphaBits); + maxBits = std::max(maxBits, formatInfo.redBits); + maxBits = std::max(maxBits, formatInfo.greenBits); + maxBits = std::max(maxBits, formatInfo.blueBits); + maxBits = std::max(maxBits, formatInfo.luminanceBits); + maxBits = std::max(maxBits, formatInfo.depthBits); + }} + + maxBits = roundUp(maxBits, 8U); + + const SwizzleFormatInfo &swizzleInfo = + GetSwizzleFormatInfo(maxBits, formatInfo.componentType); + info.swizzleTexFormat = swizzleInfo.mTexFormat; + info.swizzleSRVFormat = swizzleInfo.mSRVFormat; + info.swizzleRTVFormat = swizzleInfo.mRTVFormat; + }} + else + {{ + // The original texture format is suitable for swizzle operations + info.swizzleTexFormat = texFormat; + info.swizzleSRVFormat = srvFormat; + info.swizzleRTVFormat = rtvFormat; + }} + }} + else + {{ + // Not possible to swizzle with this texture format since it is either unsized or GL_NONE + info.swizzleTexFormat = DXGI_FORMAT_UNKNOWN; + info.swizzleSRVFormat = DXGI_FORMAT_UNKNOWN; + info.swizzleRTVFormat = DXGI_FORMAT_UNKNOWN; + }} + + // Check if there is an initialization function for this texture format + info.dataInitializerFunction = GetInternalFormatInitializer(internalFormat, texFormat); + // Gather all the load functions for this internal format + info.loadFunctions = GetLoadFunctionsMap(internalFormat, texFormat); + + ASSERT(info.loadFunctions.size() != 0 || internalFormat == GL_NONE); + + return info; +}} + +}} // namespace + +TextureFormat::TextureFormat() + : texFormat(DXGI_FORMAT_UNKNOWN), + srvFormat(DXGI_FORMAT_UNKNOWN), + rtvFormat(DXGI_FORMAT_UNKNOWN), + dsvFormat(DXGI_FORMAT_UNKNOWN), + renderFormat(DXGI_FORMAT_UNKNOWN), + swizzleTexFormat(DXGI_FORMAT_UNKNOWN), + swizzleSRVFormat(DXGI_FORMAT_UNKNOWN), + swizzleRTVFormat(DXGI_FORMAT_UNKNOWN), + dataInitializerFunction(NULL), + loadFunctions() +{{ +}} + +const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, + const Renderer11DeviceCaps &renderer11DeviceCaps) +{{ + // clang-format off + switch (internalFormat) + {{ +{data} + default: + break; + }} + // clang-format on + + static const TextureFormat defaultInfo; + return defaultInfo; +}} // GetTextureFormatInfo + +}} // namespace d3d11 + +}} // namespace rx +""" + +tex_format = "texFormat" +srv_format = "srvFormat" +rtv_format = "rtvFormat" +dsv_format = "dsvFormat" + +def get_texture_format_item(idx, texture_format): + table_data = ''; + + if idx == 0: + table_data += ' if (' + texture_format["requirementsFcn"] + '(renderer11DeviceCaps))\n' + else: + table_data += ' else if (' + texture_format["requirementsFcn"] + '(renderer11DeviceCaps))\n' + table_data += ' {\n' + table_data += ' static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat,\n' + table_data += ' ' + texture_format[tex_format] + ',\n' + table_data += ' ' + texture_format[srv_format] + ',\n' + table_data += ' ' + texture_format[rtv_format] + ',\n' + table_data += ' ' + texture_format[dsv_format] + ');\n' + table_data += ' return textureFormat;\n' + table_data += ' }\n' + + return table_data + +def parse_json_into_switch_string(json_data): + table_data = '' + for internal_format_item in sorted(json_data.iteritems()): + internal_format = internal_format_item[0] + table_data += ' case ' + internal_format + ':\n' + table_data += ' {\n' + + for idx, texture_format in enumerate(sorted(json_data[internal_format])): + table_data += get_texture_format_item(idx, texture_format) + + table_data += ' else\n' + table_data += ' {\n' + table_data += ' break;\n' + table_data += ' }\n' + table_data += ' }\n' + + return table_data + +with open('texture_format_data.json') as texture_format_json_file: + texture_format_data = texture_format_json_file.read(); + texture_format_json_file.close() + json_data = json.loads(texture_format_data) + + table_data = parse_json_into_switch_string(json_data) + output = template.format(data=table_data) + + with open('texture_format_table_autogen.cpp', 'wt') as out_file: + out_file.write(output) + out_file.close() diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.cpp new file mode 100644 index 000000000000..adb20a5e60fd --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.cpp @@ -0,0 +1,170 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// internal_format_initializer_table: +// Contains table to go from internal format and dxgi format to initializer function +// for TextureFormat +// + +#include "libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h" +#include "libANGLE/renderer/d3d/loadimage.h" + +namespace rx +{ + +namespace d3d11 +{ + +// TODO: This should be generated by a JSON file +InitializeTextureDataFunction GetInternalFormatInitializer(GLenum internalFormat, + DXGI_FORMAT dxgiFormat) +{ + switch (internalFormat) + { + case GL_RGB8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB565: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_SRGB8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB16F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB8UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB8I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_SINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB16UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_UINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB16I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_SINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB32UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_UINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + case GL_RGB32I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_SINT: + { + return Initialize4ComponentData; + } + default: + break; + } + } + default: + { + return nullptr; + } + } +} + +} // namespace d3d11 + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h new file mode 100644 index 000000000000..2d538e1d82ff --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h @@ -0,0 +1,31 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// internal_format_initializer_table: +// Contains table to go from internal format and dxgi format to initializer function +// for TextureFormat +// + +#ifndef LIBANGLE_RENDERER_D3D_D3D11_INTERNALFORMATINITIALIZERTABLE_H_ +#define LIBANGLE_RENDERER_D3D_D3D11_INTERNALFORMATINITIALIZERTABLE_H_ + +#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" + +#include + +namespace rx +{ + +namespace d3d11 +{ + +InitializeTextureDataFunction GetInternalFormatInitializer(GLenum internalFormat, + DXGI_FORMAT dxgiFormat); + +} // namespace d3d11 + +} // namespace rx + +#endif // LIBANGLE_RENDERER_D3D_D3D11_INTERNALFORMATINITIALIZERTABLE_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json new file mode 100644 index 000000000000..cb1d8595a233 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_data.json @@ -0,0 +1,1107 @@ +{ + "GL_RG8_SNORM": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8_SNORM", + "requiresConversion": "false" + } + ] + }, + "GL_SRGB8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA8I": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_R8_SNORM": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8_SNORM", + "requiresConversion": "false" + } + ] + }, + "GL_RGBA8_SNORM": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "requiresConversion": "false" + } + ] + }, + "GL_R16I": { + "GL_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2SRGBA8ToSRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2RGB8A1ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGB32UI": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "requiresConversion": "true" + } + ] + }, + "GL_ALPHA32F_EXT": { + "GL_FLOAT": [ + { + "loadFunction": "LoadA32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_R16UI": { + "GL_UNSIGNED_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_RGB9_E5": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadRGB16FToRGB9E5", + "dxgiFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_INT_5_9_9_9_REV": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "requiresConversion": "false" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadRGB32FToRGB9E5", + "dxgiFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadRGB16FToRGB9E5", + "dxgiFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_R11_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadEACR11ToR8", + "dxgiFormat": "DXGI_FORMAT_R8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA32UI": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_RG8UI": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE32F_EXT": { + "GL_FLOAT": [ + { + "loadFunction": "LoadL32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2SRGB8A1ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "requiresConversion": "true" + } + ] + }, + "GL_R16F": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16_FLOAT", + "requiresConversion": "false" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "Load32FTo16F<1>", + "dxgiFormat": "DXGI_FORMAT_R16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_RGBA8UI": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_BGRA4_ANGLEX": { + "GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT": [ + { + "loadFunction": "LoadRGBA4ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "false" + } + ] + }, + "GL_RGBA16F": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "false" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "Load32FTo16F<4>", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE8_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadL8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadCompressedToNative<4,4,16>", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RGB": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_SHORT_5_6_5": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RGB5_A1": { + "GL_UNSIGNED_INT_2_10_10_10_REV": [ + { + "loadFunction": "LoadRGB10A2ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "false" + } + ], + "GL_UNSIGNED_SHORT_5_5_5_1": [ + { + "loadFunction": "LoadRGB5A1ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + }, + { + "loadFunction": "LoadRGB5A1ToA1RGB5", + "dxgiFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGB16UI": { + "GL_UNSIGNED_SHORT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "requiresConversion": "true" + } + ] + }, + "GL_BGRA_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RGB8_ETC2": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2RGB8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA32F": { + "GL_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_RGBA32I": { + "GL_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE8_ALPHA8_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadLA8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RG8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_RGB10_A2": { + "GL_UNSIGNED_INT_2_10_10_10_REV": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_COMPRESSED_SIGNED_RG11_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadEACRG11SToRG8", + "dxgiFormat": "DXGI_FORMAT_R8G8_SNORM", + "requiresConversion": "true" + } + ] + }, + "GL_DEPTH_COMPONENT16": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadR32ToR16", + "dxgiFormat": "DXGI_FORMAT_R16_TYPELESS", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16_TYPELESS", + "requiresConversion": "false" + }, + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_D16_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_RGB32I": { + "GL_INT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "requiresConversion": "true" + } + ] + }, + "GL_R8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_RGB32F": { + "GL_FLOAT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_R11F_G11F_B10F": { + "GL_UNSIGNED_INT_10F_11F_11F_REV": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "requiresConversion": "false" + } + ], + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadRGB16FToRG11B10F", + "dxgiFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "requiresConversion": "true" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadRGB32FToRG11B10F", + "dxgiFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadRGB16FToRG11B10F", + "dxgiFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_RGB8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_LUMINANCE_ALPHA": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadLA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadLA32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadLA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA16I": { + "GL_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_R8I": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_RGB8_SNORM": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RG32F": { + "GL_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_DEPTH_COMPONENT32F": { + "GL_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32_TYPELESS", + "requiresConversion": "false" + }, + { + "loadFunction": "UnimplementedLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RG32I": { + "GL_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_ALPHA8_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_A8_UNORM", + "requiresConversion": "false" + }, + { + "loadFunction": "LoadA8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RG32UI": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G32_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_RGBA16UI": { + "GL_UNSIGNED_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_COMPRESSED_RGBA8_ETC2_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2RGBA8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGB8I": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_SRGB8_ETC2": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC2SRGB8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "requiresConversion": "true" + } + ] + }, + "GL_DEPTH32F_STENCIL8": { + "GL_FLOAT_32_UNSIGNED_INT_24_8_REV": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32G8X24_TYPELESS", + "requiresConversion": "false" + }, + { + "loadFunction": "UnimplementedLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RG8I": { + "GL_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_R32UI": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_BGR5_A1_ANGLEX": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "false" + } + ], + "GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT": [ + { + "loadFunction": "LoadRGB5A1ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RG11_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadEACRG11ToRG8", + "dxgiFormat": "DXGI_FORMAT_R8G8_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_SRGB8_ALPHA8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE_ALPHA16F_EXT": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadLA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadLA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_SHORT_4_4_4_4": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_SHORT_5_5_5_1": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_DEPTH24_STENCIL8": { + "GL_UNSIGNED_INT_24_8": [ + { + "loadFunction": "LoadR32ToR24G8", + "dxgiFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "requiresConversion": "true" + }, + { + "loadFunction": "LoadR32ToR24G8", + "dxgiFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requiresConversion": "true" + } + ] + }, + "GL_RGB16I": { + "GL_SHORT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "requiresConversion": "true" + } + ] + }, + "GL_R8UI": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_ALPHA": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadA32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_RGB16F": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadRGB32FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_SIGNED_R11_EAC": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadEACR11SToR8", + "dxgiFormat": "DXGI_FORMAT_R8_SNORM", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RGB_S3TC_DXT1_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadCompressedToNative<4,4,8>", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadCompressedToNative<4,4,8>", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_STENCIL_INDEX8": { + "DXGI_FORMAT_R24G8_TYPELESS": [ + { + "loadFunction": "UnimplementedLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "DXGI_FORMAT_D24_UNORM_S8_UINT": [ + { + "loadFunction": "UnimplementedLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_LUMINANCE_ALPHA32F_EXT": { + "GL_FLOAT": [ + { + "loadFunction": "LoadLA32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RGB8UI": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "requiresConversion": "true" + } + ] + }, + "GL_DEPTH_COMPONENT24": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadR32ToR24G8", + "dxgiFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "requiresConversion": "true" + }, + { + "loadFunction": "LoadR32ToR24G8", + "dxgiFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requiresConversion": "true" + } + ] + }, + "GL_R32I": { + "GL_INT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_DEPTH_COMPONENT32_OES": { + "GL_UNSIGNED_INT": [ + { + "loadFunction": "LoadR32ToR24G8", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_R32F": { + "GL_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R32_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_RG16F": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16_FLOAT", + "requiresConversion": "false" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "Load32FTo16F<2>", + "dxgiFormat": "DXGI_FORMAT_R16G16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16_FLOAT", + "requiresConversion": "false" + } + ] + }, + "GL_RGB565": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative3To4", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_SHORT_5_6_5": [ + { + "loadFunction": "LoadR5G6B5ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + }, + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_B5G6R5_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE16F_EXT": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadL16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadL16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RG16UI": { + "GL_UNSIGNED_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadCompressedToNative<4,4,16>", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RG16I": { + "GL_SHORT": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R16G16_SINT", + "requiresConversion": "false" + } + ] + }, + "GL_BGRA8_EXT": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "false" + } + ] + }, + "GL_ALPHA16F_EXT": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadA16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA4": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "false" + } + ], + "GL_UNSIGNED_SHORT_4_4_4_4": [ + { + "loadFunction": "LoadRGBA4ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + }, + { + "loadFunction": "LoadRGBA4ToARGB4", + "dxgiFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", + "requiresConversion": "true" + } + ] + }, + "GL_RGBA8": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "false" + } + ] + }, + "GL_LUMINANCE": { + "GL_HALF_FLOAT": [ + { + "loadFunction": "LoadL16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ], + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "UnreachableLoadFunction", + "dxgiFormat": "DXGI_FORMAT_UNKNOWN", + "requiresConversion": "true" + } + ], + "GL_FLOAT": [ + { + "loadFunction": "LoadL32FToRGBA32F", + "dxgiFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "requiresConversion": "true" + } + ], + "GL_HALF_FLOAT_OES": [ + { + "loadFunction": "LoadL16FToRGBA16F", + "dxgiFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "requiresConversion": "true" + } + ] + }, + "GL_RGB10_A2UI": { + "GL_UNSIGNED_INT_2_10_10_10_REV": [ + { + "loadFunction": "LoadToNative", + "dxgiFormat": "DXGI_FORMAT_R10G10B10A2_UINT", + "requiresConversion": "false" + } + ] + }, + "GL_ETC1_RGB8_OES": { + "GL_UNSIGNED_BYTE": [ + { + "loadFunction": "LoadETC1RGB8ToRGBA8", + "dxgiFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "requiresConversion": "true" + } + ] + } +} \ No newline at end of file diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h new file mode 100644 index 000000000000..60ab41fd838c --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table.h @@ -0,0 +1,30 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// load_functions_table: +// Contains load functions table depending on internal format and dxgi format +// + +#ifndef LIBANGLE_RENDERER_D3D_D3D11_LOADFUNCTIONSTABLE_H_ +#define LIBANGLE_RENDERER_D3D_D3D11_LOADFUNCTIONSTABLE_H_ + +#include + +#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" + +namespace rx +{ + +namespace d3d11 +{ + +const std::map &GetLoadFunctionsMap(GLenum internalFormat, + DXGI_FORMAT dxgiFormat); + +} // namespace d3d11 + +} // namespace rx + +#endif // LIBANGLE_RENDERER_D3D_D3D11_LOADFUNCTIONSTABLE_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp new file mode 100644 index 000000000000..788f792e7b27 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp @@ -0,0 +1,2079 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by gen_load_functions_table.py using data from load_functions_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// load_functions_table: +// Contains the GetLoadFunctionsMap for texture_format_util.h +// + +#include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" +#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/loadimage.h" +#include "libANGLE/renderer/d3d/loadimage_etc.h" + +namespace rx +{ + +namespace d3d11 +{ + +namespace +{ + +// ES3 image loading functions vary based on: +// - the GL internal format (supplied to glTex*Image*D) +// - the GL data type given (supplied to glTex*Image*D) +// - the target DXGI_FORMAT that the image will be loaded into (which is chosen based on the D3D +// device's capabilities) +// This map type determines which loading function to use, based on these three parameters. +// Source formats and types are taken from Tables 3.2 and 3.3 of the ES 3 spec. +void UnimplementedLoadFunction(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + UNIMPLEMENTED(); +} + +void UnreachableLoadFunction(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + UNREACHABLE(); +} + +} // namespace + +// TODO we can replace these maps with more generated code +const std::map &GetLoadFunctionsMap(GLenum internalFormat, + DXGI_FORMAT dxgiFormat) +{ + // clang-format off + switch (internalFormat) + { + case GL_ALPHA: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadA32FToRGBA32F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_ALPHA16F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadA16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_ALPHA32F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadA32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadA32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_ALPHA8_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadA8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_BGR5_A1_ANGLEX: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadRGB5A1ToRGBA8; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + loadMap[GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT] = LoadRGB5A1ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_BGRA4_ANGLEX: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadRGBA4ToRGBA8; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT] = LoadRGBA4ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_BGRA8_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_BGRA_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_COMPRESSED_R11_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadEACR11ToR8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_RG11_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadEACRG11ToRG8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_RGB8_ETC2: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGB8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGB8A1ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_RGBA8_ETC2_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2RGBA8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,16>; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadCompressedToNative<4,4,8>; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_COMPRESSED_SIGNED_R11_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadEACR11SToR8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_SIGNED_RG11_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadEACRG11SToRG8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGBA8ToSRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_SRGB8_ETC2: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGB8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC2SRGB8A1ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_DEPTH24_STENCIL8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_D24_UNORM_S8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_24_8] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R24G8_TYPELESS: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_24_8] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_DEPTH32F_STENCIL8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G8X24_TYPELESS: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT_32_UNSIGNED_INT_24_8_REV] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_DEPTH_COMPONENT16: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_D16_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R16_TYPELESS: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadR32ToR16; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_DEPTH_COMPONENT24: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_D24_UNORM_S8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R24G8_TYPELESS: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_DEPTH_COMPONENT32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32_TYPELESS: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_DEPTH_COMPONENT32_OES: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadR32ToR24G8; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_ETC1_RGB8_OES: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadETC1RGB8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_LUMINANCE: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadL32FToRGBA32F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE16F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadL16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadL16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE32F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadL32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadL32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE8_ALPHA8_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadLA8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadLA8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE8_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadL8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadL8ToRGBA8; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE_ALPHA: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE_ALPHA16F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadLA16FToRGBA16F; + loadMap[GL_HALF_FLOAT_OES] = LoadLA16FToRGBA16F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_LUMINANCE_ALPHA32F_EXT: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadLA32FToRGBA32F; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_R11F_G11F_B10F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R11G11B10_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadRGB16FToRG11B10F; + loadMap[GL_HALF_FLOAT_OES] = LoadRGB16FToRG11B10F; + loadMap[GL_FLOAT] = LoadRGB32FToRG11B10F; + loadMap[GL_UNSIGNED_INT_10F_11F_11F_REV] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R16F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = Load32FTo16F<1>; + loadMap[GL_HALF_FLOAT] = LoadToNative; + loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R16I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R16UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R32I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R32UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R8I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R8UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_R8_SNORM: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG16F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = Load32FTo16F<2>; + loadMap[GL_HALF_FLOAT] = LoadToNative; + loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG16I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG16UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG32I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG32UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG8I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG8UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RG8_SNORM: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_RGB10_A2: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R10G10B10A2_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB10_A2UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R10G10B10A2_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB16F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadRGB32FToRGBA16F; + loadMap[GL_HALF_FLOAT] = LoadToNative3To4; + loadMap[GL_HALF_FLOAT_OES] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB16I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB16UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB32I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB32UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB565: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_B5G6R5_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_6_5] = LoadR5G6B5ToRGBA8; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB5_A1: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadRGB5A1ToA1RGB5; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT_2_10_10_10_REV] = LoadRGB10A2ToRGBA8; + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = LoadRGB5A1ToRGBA8; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB8I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB8UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB8_SNORM: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGB9_E5: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_HALF_FLOAT] = LoadRGB16FToRGB9E5; + loadMap[GL_HALF_FLOAT_OES] = LoadRGB16FToRGB9E5; + loadMap[GL_FLOAT] = LoadRGB32FToRGB9E5; + loadMap[GL_UNSIGNED_INT_5_9_9_9_REV] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = UnreachableLoadFunction; + loadMap[GL_UNSIGNED_SHORT_5_5_5_1] = UnreachableLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + case GL_RGBA16F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = Load32FTo16F<4>; + loadMap[GL_HALF_FLOAT] = LoadToNative; + loadMap[GL_HALF_FLOAT_OES] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA16I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA16UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R16G16B16A16_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA32F: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_FLOAT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA32I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA32UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R32G32B32A32_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_INT] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA4: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_B4G4R4A4_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadRGBA4ToARGB4; + return loadMap; + }(); + + return loadFunctionsMap; + } + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_SHORT_4_4_4_4] = LoadRGBA4ToRGBA8; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA8I: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_SINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA8UI: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UINT: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_RGBA8_SNORM: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_SNORM: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_SRGB8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative3To4; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_SRGB8_ALPHA8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[GL_UNSIGNED_BYTE] = LoadToNative; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + break; + } + } + case GL_STENCIL_INDEX8: + { + switch (dxgiFormat) + { + case DXGI_FORMAT_UNKNOWN: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = UnimplementedLoadFunction; + loadMap[DXGI_FORMAT_R24G8_TYPELESS] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + default: + { + static const std::map loadFunctionsMap = []() { + std::map loadMap; + loadMap[DXGI_FORMAT_D24_UNORM_S8_UINT] = UnimplementedLoadFunction; + loadMap[DXGI_FORMAT_R24G8_TYPELESS] = UnimplementedLoadFunction; + return loadMap; + }(); + + return loadFunctionsMap; + } + } + } + + default: + { + static std::map emptyLoadFunctionsMap; + return emptyLoadFunctionsMap; + } + } + // clang-format on + +} // GetLoadFunctionsMap + +} // namespace d3d11 + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp index 1499bf29f9d3..e8058cc631a5 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp @@ -1070,7 +1070,8 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons } // GL core feature limits - caps->maxElementIndex = static_cast(std::numeric_limits::max()); + // Reserve MAX_UINT for D3D11's primitive restart. + caps->maxElementIndex = static_cast(std::numeric_limits::max() - 1); caps->max3DTextureSize = static_cast(GetMaximum3DTextureSize(featureLevel)); caps->max2DTextureSize = static_cast(GetMaximum2DTextureSize(featureLevel)); caps->maxCubeMapTextureSize = static_cast(GetMaximumCubeMapTextureSize(featureLevel)); @@ -1208,13 +1209,14 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons extensions->shaderTextureLOD = GetShaderTextureLODSupport(featureLevel); extensions->fragDepth = true; extensions->textureUsage = true; // This could be false since it has no effect in D3D11 - extensions->discardFramebuffer = false; // TODO: enable this once BUG:497445 is fixed (Chrome WebGL video tests fail with this extension active) + extensions->discardFramebuffer = true; extensions->translatedShaderSource = true; extensions->fboRenderMipmap = false; extensions->debugMarker = true; extensions->eglImage = true; extensions->unpackSubimage = true; extensions->packSubimage = true; + extensions->vertexArrayObject = true; // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing. // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing. @@ -1298,6 +1300,11 @@ void GenerateInitialTextureData(GLint internalFormat, const Renderer11DeviceCaps } } +UINT GetPrimitiveRestartIndex() +{ + return std::numeric_limits::max(); +} + void SetPositionTexCoordVertex(PositionTexCoordVertex* vertex, float x, float y, float u, float v) { vertex->x = x; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h index 3949b644c2fa..6d6591e45c9a 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h @@ -71,6 +71,8 @@ void GenerateInitialTextureData(GLint internalFormat, const Renderer11DeviceCaps GLuint mipLevels, std::vector *outSubresourceData, std::vector< std::vector > *outData); +UINT GetPrimitiveRestartIndex(); + struct PositionTexCoordVertex { float x, y; diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_data.json b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_data.json new file mode 100644 index 000000000000..3e9e6877d99c --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_data.json @@ -0,0 +1,77 @@ +{ + "GL_UNSIGNED_NORMALIZED": { + "8": { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM" + }, + "16": { + "texFormat": "DXGI_FORMAT_R16G16B16A16_UNORM", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_UNORM", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UNORM" + }, + "24": { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" + }, + "32": { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" + } + }, + "GL_SIGNED_NORMALIZED": { + "8": { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM" + } + }, + "GL_FLOAT": { + "16": { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT" + }, + "32": { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT" + } + }, + "GL_UNSIGNED_INT": { + "8": { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT" + }, + "16": { + "texFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT" + }, + "32": { + "texFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT" + } + }, + "GL_INT": { + "8": { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT" + }, + "16": { + "texFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT" + }, + "32": { + "texFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT" + } + } +} \ No newline at end of file diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.cpp deleted file mode 100644 index 39b0ad70c2e4..000000000000 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright 2015 The ANGLE Project Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// swizzle_format_info: -// Provides information for swizzle format and a map from type->formatinfo -// - -#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" - -#include - -namespace rx -{ - -namespace d3d11 -{ - -SwizzleSizeType::SwizzleSizeType() : maxComponentSize(0), componentType(GL_NONE) -{ -} - -SwizzleSizeType::SwizzleSizeType(size_t maxComponentSize, GLenum componentType) - : maxComponentSize(maxComponentSize), componentType(componentType) -{ -} - -bool SwizzleSizeType::operator<(const SwizzleSizeType &other) const -{ - return (maxComponentSize != other.maxComponentSize) - ? (maxComponentSize < other.maxComponentSize) - : (componentType < other.componentType); -} - -SwizzleFormatInfo::SwizzleFormatInfo() - : mTexFormat(DXGI_FORMAT_UNKNOWN), - mSRVFormat(DXGI_FORMAT_UNKNOWN), - mRTVFormat(DXGI_FORMAT_UNKNOWN) -{ -} - -SwizzleFormatInfo::SwizzleFormatInfo(DXGI_FORMAT texFormat, - DXGI_FORMAT srvFormat, - DXGI_FORMAT rtvFormat) - : mTexFormat(texFormat), mSRVFormat(srvFormat), mRTVFormat(rtvFormat) -{ -} - -typedef std::pair SwizzleInfoPair; - -const SwizzleInfoMap &BuildSwizzleInfoMap() -{ - static SwizzleInfoMap map; - - map.insert( - SwizzleInfoPair(SwizzleSizeType(8, GL_UNSIGNED_NORMALIZED), - SwizzleFormatInfo(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(16, GL_UNSIGNED_NORMALIZED), - SwizzleFormatInfo(DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, - DXGI_FORMAT_R16G16B16A16_UNORM))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(24, GL_UNSIGNED_NORMALIZED), - SwizzleFormatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, - DXGI_FORMAT_R32G32B32A32_FLOAT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(32, GL_UNSIGNED_NORMALIZED), - SwizzleFormatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, - DXGI_FORMAT_R32G32B32A32_FLOAT))); - - map.insert( - SwizzleInfoPair(SwizzleSizeType(8, GL_SIGNED_NORMALIZED), - SwizzleFormatInfo(DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_SNORM, - DXGI_FORMAT_R8G8B8A8_SNORM))); - - map.insert(SwizzleInfoPair( - SwizzleSizeType(16, GL_FLOAT), - SwizzleFormatInfo(DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, - DXGI_FORMAT_R16G16B16A16_FLOAT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(32, GL_FLOAT), - SwizzleFormatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, - DXGI_FORMAT_R32G32B32A32_FLOAT))); - - map.insert( - SwizzleInfoPair(SwizzleSizeType(8, GL_UNSIGNED_INT), - SwizzleFormatInfo(DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_UINT, - DXGI_FORMAT_R8G8B8A8_UINT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(16, GL_UNSIGNED_INT), - SwizzleFormatInfo(DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_UINT, - DXGI_FORMAT_R16G16B16A16_UINT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(32, GL_UNSIGNED_INT), - SwizzleFormatInfo(DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_R32G32B32A32_UINT, - DXGI_FORMAT_R32G32B32A32_UINT))); - - map.insert( - SwizzleInfoPair(SwizzleSizeType(8, GL_INT), - SwizzleFormatInfo(DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R8G8B8A8_SINT, - DXGI_FORMAT_R8G8B8A8_SINT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(16, GL_INT), - SwizzleFormatInfo(DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_R16G16B16A16_SINT, - DXGI_FORMAT_R16G16B16A16_SINT))); - map.insert(SwizzleInfoPair( - SwizzleSizeType(32, GL_INT), - SwizzleFormatInfo(DXGI_FORMAT_R32G32B32A32_SINT, DXGI_FORMAT_R32G32B32A32_SINT, - DXGI_FORMAT_R32G32B32A32_SINT))); - - return map; -} - -} // namespace d3d11 - -} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.h index 4b1f0244950b..df9a30ff5059 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info.h @@ -42,9 +42,7 @@ struct SwizzleFormatInfo SwizzleFormatInfo(DXGI_FORMAT texFormat, DXGI_FORMAT srvFormat, DXGI_FORMAT rtvFormat); }; -typedef std::map SwizzleInfoMap; - -const SwizzleInfoMap &BuildSwizzleInfoMap(); +const SwizzleFormatInfo &GetSwizzleFormatInfo(GLuint maxBits, GLenum componentType); } // namespace d3d11 diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info_autogen.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info_autogen.cpp new file mode 100644 index 000000000000..84d6fada9761 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/swizzle_format_info_autogen.cpp @@ -0,0 +1,203 @@ +// GENERATED FILE - DO NOT EDIT +// Generated by gen_swizzle_format_table.py using data from swizzle_format_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// swizzle_format_info: +// Provides information for swizzle format and a map from type->formatinfo +// + +#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" + +#include + +namespace rx +{ + +namespace d3d11 +{ + +SwizzleSizeType::SwizzleSizeType() : maxComponentSize(0), componentType(GL_NONE) +{ +} + +SwizzleSizeType::SwizzleSizeType(size_t maxComponentSize, GLenum componentType) + : maxComponentSize(maxComponentSize), componentType(componentType) +{ +} + +bool SwizzleSizeType::operator<(const SwizzleSizeType &other) const +{ + return (maxComponentSize != other.maxComponentSize) + ? (maxComponentSize < other.maxComponentSize) + : (componentType < other.componentType); +} + +SwizzleFormatInfo::SwizzleFormatInfo() + : mTexFormat(DXGI_FORMAT_UNKNOWN), + mSRVFormat(DXGI_FORMAT_UNKNOWN), + mRTVFormat(DXGI_FORMAT_UNKNOWN) +{ +} + +SwizzleFormatInfo::SwizzleFormatInfo(DXGI_FORMAT texFormat, + DXGI_FORMAT srvFormat, + DXGI_FORMAT rtvFormat) + : mTexFormat(texFormat), mSRVFormat(srvFormat), mRTVFormat(rtvFormat) +{ +} + +const SwizzleFormatInfo &GetSwizzleFormatInfo(GLuint maxBits, GLenum componentType) +{ + // clang-format off + switch (componentType) + { + case GL_FLOAT: + { + switch (maxBits) + { + case 16: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT); + return formatInfo; + } + case 32: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT); + return formatInfo; + } + default: + break; + } + } + case GL_INT: + { + switch (maxBits) + { + case 16: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT); + return formatInfo; + } + case 32: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT); + return formatInfo; + } + case 8: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT); + return formatInfo; + } + default: + break; + } + } + case GL_SIGNED_NORMALIZED: + { + switch (maxBits) + { + case 8: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_R8G8B8A8_SNORM); + return formatInfo; + } + default: + break; + } + } + case GL_UNSIGNED_INT: + { + switch (maxBits) + { + case 16: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT); + return formatInfo; + } + case 32: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT); + return formatInfo; + } + case 8: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT); + return formatInfo; + } + default: + break; + } + } + case GL_UNSIGNED_NORMALIZED: + { + switch (maxBits) + { + case 16: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R16G16B16A16_UNORM, + DXGI_FORMAT_R16G16B16A16_UNORM, + DXGI_FORMAT_R16G16B16A16_UNORM); + return formatInfo; + } + case 24: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT); + return formatInfo; + } + case 32: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT); + return formatInfo; + } + case 8: + { + static const SwizzleFormatInfo formatInfo(DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM); + return formatInfo; + } + default: + break; + } + } + + default: + { + static const SwizzleFormatInfo defaultInfo(DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return defaultInfo; + } + } + // clang-format on + +} // GetSwizzleFormatInfo + +} // namespace d3d11 + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json new file mode 100644 index 000000000000..ca84a0045603 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json @@ -0,0 +1,889 @@ +{ + "GL_ALPHA": [ + { + "texFormat": "DXGI_FORMAT_A8_UNORM", + "srvFormat": "DXGI_FORMAT_A8_UNORM", + "rtvFormat": "DXGI_FORMAT_A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_ALPHA16F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_ALPHA32F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_ALPHA8_EXT": [ + { + "texFormat": "DXGI_FORMAT_A8_UNORM", + "srvFormat": "DXGI_FORMAT_A8_UNORM", + "rtvFormat": "DXGI_FORMAT_A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_BGR5_A1_ANGLEX": [ + { + "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_BGRA4_ANGLEX": [ + { + "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_BGRA8_EXT": [ + { + "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_BGRA_EXT": [ + { + "texFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "srvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_B8G8R8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_COMPRESSED_R11_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8_UNORM", + "srvFormat": "DXGI_FORMAT_R8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_RG11_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8G8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_RGB8_ETC2": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_RGBA8_ETC2_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT": [ + { + "texFormat": "DXGI_FORMAT_BC1_UNORM", + "srvFormat": "DXGI_FORMAT_BC1_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE": [ + { + "texFormat": "DXGI_FORMAT_BC2_UNORM", + "srvFormat": "DXGI_FORMAT_BC2_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE": [ + { + "texFormat": "DXGI_FORMAT_BC3_UNORM", + "srvFormat": "DXGI_FORMAT_BC3_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_COMPRESSED_RGB_S3TC_DXT1_EXT": [ + { + "texFormat": "DXGI_FORMAT_BC1_UNORM", + "srvFormat": "DXGI_FORMAT_BC1_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_COMPRESSED_SIGNED_R11_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8_SNORM", + "srvFormat": "DXGI_FORMAT_R8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_SIGNED_RG11_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8G8_SNORM", + "srvFormat": "DXGI_FORMAT_R8G8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_SRGB8_ETC2": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_DEPTH24_STENCIL8": [ + { + "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_DEPTH32F_STENCIL8": [ + { + "texFormat": "DXGI_FORMAT_R32G8X24_TYPELESS", + "srvFormat": "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D32_FLOAT_S8X24_UINT", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_UNKNOWN", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_DEPTH_COMPONENT16": [ + { + "texFormat": "DXGI_FORMAT_R16_TYPELESS", + "srvFormat": "DXGI_FORMAT_R16_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D16_UNORM", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_D16_UNORM", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D16_UNORM", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_DEPTH_COMPONENT24": [ + { + "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_DEPTH_COMPONENT32F": [ + { + "texFormat": "DXGI_FORMAT_R32_TYPELESS", + "srvFormat": "DXGI_FORMAT_R32_FLOAT", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D32_FLOAT", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_UNKNOWN", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "OnlyFL9_3" + } + ], + "GL_DEPTH_COMPONENT32_OES": [ + { + "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "srvFormat": "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL10Plus" + } + ], + "GL_ETC1_RGB8_OES": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE16F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE32F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE8_ALPHA8_EXT": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE8_EXT": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE_ALPHA": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE_ALPHA16F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_LUMINANCE_ALPHA32F_EXT": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_NONE": [ + { + "texFormat": "DXGI_FORMAT_UNKNOWN", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R11F_G11F_B10F": [ + { + "texFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "srvFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "rtvFormat": "DXGI_FORMAT_R11G11B10_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R16F": [ + { + "texFormat": "DXGI_FORMAT_R16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R16I": [ + { + "texFormat": "DXGI_FORMAT_R16_SINT", + "srvFormat": "DXGI_FORMAT_R16_SINT", + "rtvFormat": "DXGI_FORMAT_R16_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R16UI": [ + { + "texFormat": "DXGI_FORMAT_R16_UINT", + "srvFormat": "DXGI_FORMAT_R16_UINT", + "rtvFormat": "DXGI_FORMAT_R16_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R32F": [ + { + "texFormat": "DXGI_FORMAT_R32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R32I": [ + { + "texFormat": "DXGI_FORMAT_R32_SINT", + "srvFormat": "DXGI_FORMAT_R32_SINT", + "rtvFormat": "DXGI_FORMAT_R32_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R32UI": [ + { + "texFormat": "DXGI_FORMAT_R32_UINT", + "srvFormat": "DXGI_FORMAT_R32_UINT", + "rtvFormat": "DXGI_FORMAT_R32_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R8": [ + { + "texFormat": "DXGI_FORMAT_R8_UNORM", + "srvFormat": "DXGI_FORMAT_R8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R8I": [ + { + "texFormat": "DXGI_FORMAT_R8_SINT", + "srvFormat": "DXGI_FORMAT_R8_SINT", + "rtvFormat": "DXGI_FORMAT_R8_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R8UI": [ + { + "texFormat": "DXGI_FORMAT_R8_UINT", + "srvFormat": "DXGI_FORMAT_R8_UINT", + "rtvFormat": "DXGI_FORMAT_R8_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_R8_SNORM": [ + { + "texFormat": "DXGI_FORMAT_R8_SNORM", + "srvFormat": "DXGI_FORMAT_R8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG16F": [ + { + "texFormat": "DXGI_FORMAT_R16G16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG16I": [ + { + "texFormat": "DXGI_FORMAT_R16G16_SINT", + "srvFormat": "DXGI_FORMAT_R16G16_SINT", + "rtvFormat": "DXGI_FORMAT_R16G16_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG16UI": [ + { + "texFormat": "DXGI_FORMAT_R16G16_UINT", + "srvFormat": "DXGI_FORMAT_R16G16_UINT", + "rtvFormat": "DXGI_FORMAT_R16G16_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG32F": [ + { + "texFormat": "DXGI_FORMAT_R32G32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG32I": [ + { + "texFormat": "DXGI_FORMAT_R32G32_SINT", + "srvFormat": "DXGI_FORMAT_R32G32_SINT", + "rtvFormat": "DXGI_FORMAT_R32G32_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG32UI": [ + { + "texFormat": "DXGI_FORMAT_R32G32_UINT", + "srvFormat": "DXGI_FORMAT_R32G32_UINT", + "rtvFormat": "DXGI_FORMAT_R32G32_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG8": [ + { + "texFormat": "DXGI_FORMAT_R8G8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG8I": [ + { + "texFormat": "DXGI_FORMAT_R8G8_SINT", + "srvFormat": "DXGI_FORMAT_R8G8_SINT", + "rtvFormat": "DXGI_FORMAT_R8G8_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG8UI": [ + { + "texFormat": "DXGI_FORMAT_R8G8_UINT", + "srvFormat": "DXGI_FORMAT_R8G8_UINT", + "rtvFormat": "DXGI_FORMAT_R8G8_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RG8_SNORM": [ + { + "texFormat": "DXGI_FORMAT_R8G8_SNORM", + "srvFormat": "DXGI_FORMAT_R8G8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB10_A2": [ + { + "texFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", + "srvFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", + "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB10_A2UI": [ + { + "texFormat": "DXGI_FORMAT_R10G10B10A2_UINT", + "srvFormat": "DXGI_FORMAT_R10G10B10A2_UINT", + "rtvFormat": "DXGI_FORMAT_R10G10B10A2_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB16F": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB16I": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB16UI": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB32F": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB32I": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB32UI": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB565": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + }, + { + "texFormat": "DXGI_FORMAT_B5G6R5_UNORM", + "srvFormat": "DXGI_FORMAT_B5G6R5_UNORM", + "rtvFormat": "DXGI_FORMAT_B5G6R5_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + } + ], + "GL_RGB5_A1": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + }, + { + "texFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", + "srvFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", + "rtvFormat": "DXGI_FORMAT_B5G5R5A1_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + } + ], + "GL_RGB8": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB8I": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB8UI": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB8_SNORM": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGB9_E5": [ + { + "texFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "srvFormat": "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA16F": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA16I": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA16UI": [ + { + "texFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "srvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "rtvFormat": "DXGI_FORMAT_R16G16B16A16_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA32F": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_FLOAT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA32I": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA32UI": [ + { + "texFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "srvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "rtvFormat": "DXGI_FORMAT_R32G32B32A32_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA4": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + }, + { + "texFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", + "srvFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", + "rtvFormat": "DXGI_FORMAT_B4G4R4A4_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "SupportsFormat" + } + ], + "GL_RGBA8": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA8I": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_SINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA8UI": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UINT", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_RGBA8_SNORM": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_SNORM", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_SRGB8": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_SRGB8_ALPHA8": [ + { + "texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "rtvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "dsvFormat": "DXGI_FORMAT_UNKNOWN", + "requirementsFcn": "AnyDevice" + } + ], + "GL_STENCIL_INDEX8": [ + { + "texFormat": "DXGI_FORMAT_R24G8_TYPELESS", + "srvFormat": "DXGI_FORMAT_X24_TYPELESS_G8_UINT", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL10Plus" + }, + { + "texFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "srvFormat": "DXGI_FORMAT_UNKNOWN", + "rtvFormat": "DXGI_FORMAT_UNKNOWN", + "dsvFormat": "DXGI_FORMAT_D24_UNORM_S8_UINT", + "requirementsFcn": "OnlyFL9_3" + } + ] +} \ No newline at end of file diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp index aae0d8ca86d2..c37a89150a83 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp @@ -340,16 +340,19 @@ const D3D11ES3FormatMap &BuildD3D11FormatMap() // Compressed formats, From ES 3.0.1 spec, table 3.16 // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format | Requirements - InsertD3D11FormatInfo(&map, GL_COMPRESSED_R11_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_R11_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RG11_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_RG11_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_ETC2, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ETC2, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); - InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_R11_EAC, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_R11_EAC, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_RG11_EAC, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_SIGNED_RG11_EAC, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + InsertD3D11FormatInfo(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, OnlyFL10Plus); + + // From GL_ETC1_RGB8_OES + InsertD3D11FormatInfo(&map, GL_ETC1_RGB8_OES, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); // From GL_EXT_texture_compression_dxt1 InsertD3D11FormatInfo(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, AnyDevice); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp new file mode 100644 index 000000000000..8f96289c06f4 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp @@ -0,0 +1,1775 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by gen_texture_format_table.py using data from texture_format_data.json +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// texture_format_table: +// Queries for full textureFormat information based in internalFormat +// + +#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" + +#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" +#include "libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h" +#include "libANGLE/renderer/d3d/d3d11/load_functions_table.h" +#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" +#include "libANGLE/renderer/d3d/d3d11/swizzle_format_info.h" +#include "libANGLE/renderer/d3d/loadimage.h" + +namespace rx +{ + +namespace d3d11 +{ + +namespace +{ + +typedef bool (*FormatSupportFunction)(const Renderer11DeviceCaps &); + +bool AnyDevice(const Renderer11DeviceCaps &deviceCaps) +{ + return true; +} + +bool OnlyFL10Plus(const Renderer11DeviceCaps &deviceCaps) +{ + return (deviceCaps.featureLevel >= D3D_FEATURE_LEVEL_10_0); +} + +bool OnlyFL9_3(const Renderer11DeviceCaps &deviceCaps) +{ + return (deviceCaps.featureLevel == D3D_FEATURE_LEVEL_9_3); +} + +template +bool SupportsFormat(const Renderer11DeviceCaps &deviceCaps) +{ + // Must support texture, SRV and RTV support + UINT mustSupport = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_TEXTURECUBE | + D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_MIP | + D3D11_FORMAT_SUPPORT_RENDER_TARGET; + + if (d3d11_gl::GetMaximumClientVersion(deviceCaps.featureLevel) > 2) + { + mustSupport |= D3D11_FORMAT_SUPPORT_TEXTURE3D; + } + + bool fullSupport = false; + if (format == DXGI_FORMAT_B5G6R5_UNORM) + { + // All hardware that supports DXGI_FORMAT_B5G6R5_UNORM should support autogen mipmaps, but + // check anyway. + mustSupport |= D3D11_FORMAT_SUPPORT_MIP_AUTOGEN; + fullSupport = ((deviceCaps.B5G6R5support & mustSupport) == mustSupport); + } + else if (format == DXGI_FORMAT_B4G4R4A4_UNORM) + { + fullSupport = ((deviceCaps.B4G4R4A4support & mustSupport) == mustSupport); + } + else if (format == DXGI_FORMAT_B5G5R5A1_UNORM) + { + fullSupport = ((deviceCaps.B5G5R5A1support & mustSupport) == mustSupport); + } + else + { + UNREACHABLE(); + return false; + } + + // This 'SupportsFormat' function is used by individual entries in the D3D11 Format Map below, + // which maps GL formats to DXGI formats. + if (requireSupport) + { + // This means that ANGLE would like to use the entry in the map if the inputted DXGI format + // *IS* supported. + // e.g. the entry might map GL_RGB5_A1 to DXGI_FORMAT_B5G5R5A1, which should only be used if + // DXGI_FORMAT_B5G5R5A1 is supported. + // In this case, we should only return 'true' if the format *IS* supported. + return fullSupport; + } + else + { + // This means that ANGLE would like to use the entry in the map if the inputted DXGI format + // *ISN'T* supported. + // This might be a fallback entry. e.g. for ANGLE to use DXGI_FORMAT_R8G8B8A8_UNORM if + // DXGI_FORMAT_B5G5R5A1 isn't supported. + // In this case, we should only return 'true' if the format *ISN'T* supported. + return !fullSupport; + } +} + +// End Format Support Functions + +// For sized GL internal formats, there are several possible corresponding D3D11 formats depending +// on device capabilities. +// This function allows querying for the DXGI texture formats to use for textures, SRVs, RTVs and +// DSVs given a GL internal format. +const TextureFormat GetD3D11FormatInfo(GLenum internalFormat, + DXGI_FORMAT texFormat, + DXGI_FORMAT srvFormat, + DXGI_FORMAT rtvFormat, + DXGI_FORMAT dsvFormat) +{ + TextureFormat info; + info.texFormat = texFormat; + info.srvFormat = srvFormat; + info.rtvFormat = rtvFormat; + info.dsvFormat = dsvFormat; + + // Given a GL internal format, the renderFormat is the DSV format if it is depth- or + // stencil-renderable, + // the RTV format if it is color-renderable, and the (nonrenderable) texture format otherwise. + if (dsvFormat != DXGI_FORMAT_UNKNOWN) + { + info.renderFormat = dsvFormat; + } + else if (rtvFormat != DXGI_FORMAT_UNKNOWN) + { + info.renderFormat = rtvFormat; + } + else if (texFormat != DXGI_FORMAT_UNKNOWN) + { + info.renderFormat = texFormat; + } + else + { + info.renderFormat = DXGI_FORMAT_UNKNOWN; + } + + // Compute the swizzle formats + const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat); + if (internalFormat != GL_NONE && formatInfo.pixelBytes > 0) + { + if (formatInfo.componentCount != 4 || texFormat == DXGI_FORMAT_UNKNOWN || + srvFormat == DXGI_FORMAT_UNKNOWN || rtvFormat == DXGI_FORMAT_UNKNOWN) + { + // Get the maximum sized component + unsigned int maxBits = 1; + if (formatInfo.compressed) + { + unsigned int compressedBitsPerBlock = formatInfo.pixelBytes * 8; + unsigned int blockSize = + formatInfo.compressedBlockWidth * formatInfo.compressedBlockHeight; + maxBits = std::max(compressedBitsPerBlock / blockSize, maxBits); + } + else + { + maxBits = std::max(maxBits, formatInfo.alphaBits); + maxBits = std::max(maxBits, formatInfo.redBits); + maxBits = std::max(maxBits, formatInfo.greenBits); + maxBits = std::max(maxBits, formatInfo.blueBits); + maxBits = std::max(maxBits, formatInfo.luminanceBits); + maxBits = std::max(maxBits, formatInfo.depthBits); + } + + maxBits = roundUp(maxBits, 8U); + + const SwizzleFormatInfo &swizzleInfo = + GetSwizzleFormatInfo(maxBits, formatInfo.componentType); + info.swizzleTexFormat = swizzleInfo.mTexFormat; + info.swizzleSRVFormat = swizzleInfo.mSRVFormat; + info.swizzleRTVFormat = swizzleInfo.mRTVFormat; + } + else + { + // The original texture format is suitable for swizzle operations + info.swizzleTexFormat = texFormat; + info.swizzleSRVFormat = srvFormat; + info.swizzleRTVFormat = rtvFormat; + } + } + else + { + // Not possible to swizzle with this texture format since it is either unsized or GL_NONE + info.swizzleTexFormat = DXGI_FORMAT_UNKNOWN; + info.swizzleSRVFormat = DXGI_FORMAT_UNKNOWN; + info.swizzleRTVFormat = DXGI_FORMAT_UNKNOWN; + } + + // Check if there is an initialization function for this texture format + info.dataInitializerFunction = GetInternalFormatInitializer(internalFormat, texFormat); + // Gather all the load functions for this internal format + info.loadFunctions = GetLoadFunctionsMap(internalFormat, texFormat); + + ASSERT(info.loadFunctions.size() != 0 || internalFormat == GL_NONE); + + return info; +} + +} // namespace + +TextureFormat::TextureFormat() + : texFormat(DXGI_FORMAT_UNKNOWN), + srvFormat(DXGI_FORMAT_UNKNOWN), + rtvFormat(DXGI_FORMAT_UNKNOWN), + dsvFormat(DXGI_FORMAT_UNKNOWN), + renderFormat(DXGI_FORMAT_UNKNOWN), + swizzleTexFormat(DXGI_FORMAT_UNKNOWN), + swizzleSRVFormat(DXGI_FORMAT_UNKNOWN), + swizzleRTVFormat(DXGI_FORMAT_UNKNOWN), + dataInitializerFunction(NULL), + loadFunctions() +{ +} + +const TextureFormat &GetTextureFormatInfo(GLenum internalFormat, + const Renderer11DeviceCaps &renderer11DeviceCaps) +{ + // clang-format off + switch (internalFormat) + { + case GL_ALPHA: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_ALPHA16F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_ALPHA32F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_ALPHA8_EXT: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_BGR5_A1_ANGLEX: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_BGRA4_ANGLEX: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_BGRA8_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_BGRA_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_R11_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_UNORM, + DXGI_FORMAT_R8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RG11_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_UNORM, + DXGI_FORMAT_R8G8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGB8_ETC2: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGBA8_ETC2_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_BC2_UNORM, + DXGI_FORMAT_BC2_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_BC3_UNORM, + DXGI_FORMAT_BC3_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_SIGNED_R11_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_SNORM, + DXGI_FORMAT_R8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_SIGNED_RG11_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_SNORM, + DXGI_FORMAT_R8G8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_SRGB8_ETC2: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH24_STENCIL8: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_D24_UNORM_S8_UINT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH32F_STENCIL8: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G8X24_TYPELESS, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH_COMPONENT16: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16_TYPELESS, + DXGI_FORMAT_R16_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D16_UNORM); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_D16_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D16_UNORM); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH_COMPONENT24: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_D24_UNORM_S8_UINT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH_COMPONENT32F: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32_TYPELESS, + DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D32_FLOAT); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_DEPTH_COMPONENT32_OES: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else + { + break; + } + } + case GL_ETC1_RGB8_OES: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE16F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE32F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE8_ALPHA8_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE8_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE_ALPHA: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE_ALPHA16F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_LUMINANCE_ALPHA32F_EXT: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_NONE: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R11F_G11F_B10F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R11G11B10_FLOAT, + DXGI_FORMAT_R11G11B10_FLOAT, + DXGI_FORMAT_R11G11B10_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R16F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16_FLOAT, + DXGI_FORMAT_R16_FLOAT, + DXGI_FORMAT_R16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R16I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16_SINT, + DXGI_FORMAT_R16_SINT, + DXGI_FORMAT_R16_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R16UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16_UINT, + DXGI_FORMAT_R16_UINT, + DXGI_FORMAT_R16_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R32F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_R32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R32I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32_SINT, + DXGI_FORMAT_R32_SINT, + DXGI_FORMAT_R32_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R32UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32_UINT, + DXGI_FORMAT_R32_UINT, + DXGI_FORMAT_R32_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_UNORM, + DXGI_FORMAT_R8_UNORM, + DXGI_FORMAT_R8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R8I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_SINT, + DXGI_FORMAT_R8_SINT, + DXGI_FORMAT_R8_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R8UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_UINT, + DXGI_FORMAT_R8_UINT, + DXGI_FORMAT_R8_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_R8_SNORM: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8_SNORM, + DXGI_FORMAT_R8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG16F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16_FLOAT, + DXGI_FORMAT_R16G16_FLOAT, + DXGI_FORMAT_R16G16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG16I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16_SINT, + DXGI_FORMAT_R16G16_SINT, + DXGI_FORMAT_R16G16_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG16UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16_UINT, + DXGI_FORMAT_R16G16_UINT, + DXGI_FORMAT_R16G16_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG32F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32_FLOAT, + DXGI_FORMAT_R32G32_FLOAT, + DXGI_FORMAT_R32G32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG32I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32_SINT, + DXGI_FORMAT_R32G32_SINT, + DXGI_FORMAT_R32G32_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG32UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32_UINT, + DXGI_FORMAT_R32G32_UINT, + DXGI_FORMAT_R32G32_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_UNORM, + DXGI_FORMAT_R8G8_UNORM, + DXGI_FORMAT_R8G8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG8I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_SINT, + DXGI_FORMAT_R8G8_SINT, + DXGI_FORMAT_R8G8_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG8UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_UINT, + DXGI_FORMAT_R8G8_UINT, + DXGI_FORMAT_R8G8_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RG8_SNORM: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8_SNORM, + DXGI_FORMAT_R8G8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB10_A2: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB10_A2UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R10G10B10A2_UINT, + DXGI_FORMAT_R10G10B10A2_UINT, + DXGI_FORMAT_R10G10B10A2_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB16F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB16I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB16UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB32F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB32I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB32UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB565: + { + if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B5G6R5_UNORM, + DXGI_FORMAT_B5G6R5_UNORM, + DXGI_FORMAT_B5G6R5_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB5_A1: + { + if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B5G5R5A1_UNORM, + DXGI_FORMAT_B5G5R5A1_UNORM, + DXGI_FORMAT_B5G5R5A1_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB8I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB8UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB8_SNORM: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGB9_E5: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA16F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA16I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA16UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA32F: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA32I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA32UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA4: + { + if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else if (SupportsFormat(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_B4G4R4A4_UNORM, + DXGI_FORMAT_B4G4R4A4_UNORM, + DXGI_FORMAT_B4G4R4A4_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA8I: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA8UI: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_RGBA8_SNORM: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_SRGB8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_SRGB8_ALPHA8: + { + if (AnyDevice(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_UNKNOWN); + return textureFormat; + } + else + { + break; + } + } + case GL_STENCIL_INDEX8: + { + if (OnlyFL10Plus(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_R24G8_TYPELESS, + DXGI_FORMAT_X24_TYPELESS_G8_UINT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else if (OnlyFL9_3(renderer11DeviceCaps)) + { + static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat, + DXGI_FORMAT_D24_UNORM_S8_UINT, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_D24_UNORM_S8_UINT); + return textureFormat; + } + else + { + break; + } + } + + default: + break; + } + // clang-format on + + static const TextureFormat defaultInfo; + return defaultInfo; +} // GetTextureFormatInfo + +} // namespace d3d11 + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp index 17cbcc1c8c7d..6479f4084939 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.cpp @@ -10,6 +10,7 @@ #include "libANGLE/renderer/d3d/d3d11/texture_format_util.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" #include "libANGLE/renderer/d3d/loadimage.h" +#include "libANGLE/renderer/d3d/loadimage_etc.h" namespace rx { @@ -67,6 +68,7 @@ const D3D11LoadFunctionMap &BuildD3D11LoadFunctionMap() { static D3D11LoadFunctionMap map; + // clang-format off // | Internal format | Type | Target DXGI Format | Load function | InsertLoadFunction(&map, GL_RGBA8, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative ); InsertLoadFunction(&map, GL_RGB5_A1, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadToNative ); @@ -205,18 +207,20 @@ const D3D11LoadFunctionMap &BuildD3D11LoadFunctionMap() InsertLoadFunction(&map, GL_BGR5_A1_ANGLEX, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadToNative ); // Compressed formats // From ES 3.0.1 spec, table 3.16 - // | Internal format | Type | Load function | - InsertLoadFunction(&map, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_RGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); - InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, UnimplementedLoadFunction ); + // | Internal format | Type | Target DXGI Format | Load function + InsertLoadFunction(&map, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_UNORM, LoadEACR11ToR8 ); + InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_R11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8_SNORM, LoadEACR11SToR8 ); + InsertLoadFunction(&map, GL_COMPRESSED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_UNORM, LoadEACRG11ToRG8 ); + InsertLoadFunction(&map, GL_COMPRESSED_SIGNED_RG11_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8_SNORM, LoadEACRG11SToRG8 ); + InsertLoadFunction(&map, GL_COMPRESSED_RGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGB8ToRGBA8 ); + InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGB8ToRGBA8 ); + InsertLoadFunction(&map, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGB8A1ToRGBA8 ); + InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGB8A1ToRGBA8); + InsertLoadFunction(&map, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC2RGBA8ToRGBA8 ); + InsertLoadFunction(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, LoadETC2SRGBA8ToSRGBA8); + + // From GL_ETC1_RGB8_OES + InsertLoadFunction(&map, GL_ETC1_RGB8_OES, GL_UNSIGNED_BYTE, DXGI_FORMAT_R8G8B8A8_UNORM, LoadETC1RGB8ToRGBA8 ); // From GL_EXT_texture_compression_dxt1 InsertLoadFunction(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 8> ); @@ -227,6 +231,7 @@ const D3D11LoadFunctionMap &BuildD3D11LoadFunctionMap() // From GL_ANGLE_texture_compression_dxt5 InsertLoadFunction(&map, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_UNSIGNED_BYTE, DXGI_FORMAT_UNKNOWN, LoadCompressedToNative<4, 4, 16> ); + // clang-format on return map; } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.h deleted file mode 100644 index e6c7d9923e04..000000000000 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_util.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright 2015 The ANGLE Project Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// texture_format_util: -// Contains helper functions for texture_format_table -// - -#ifndef LIBANGLE_RENDERER_D3D_D3D11_TEXTUREFORMATUTIL_H_ -#define LIBANGLE_RENDERER_D3D_D3D11_TEXTUREFORMATUTIL_H_ - -#include - -#include "libANGLE/renderer/d3d/d3d11/renderer11.h" - -namespace rx -{ - -namespace d3d11 -{ - -typedef std::pair DxgiFormatLoadFunctionPair; -typedef std::pair GLTypeDXGIFunctionPair; -typedef std::map> D3D11LoadFunctionMap; - -const D3D11LoadFunctionMap &BuildD3D11LoadFunctionMap(); - -typedef std::pair InitializeTextureFormatPair; -typedef std::map - InternalFormatInitializerMap; - -const InternalFormatInitializerMap &BuildInternalFormatInitializerMap(); - -} // namespace d3d11 - -} // namespace rx - -#endif // LIBANGLE_RENDERER_D3D_D3D11_TEXTUREFORMATUTIL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp index 993913605bab..2ac8ee3a29ca 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp @@ -505,13 +505,6 @@ gl::Error Blit9::setFormatConvertShaders(GLenum destFormat) return gl::Error(GL_NO_ERROR); } -static bool -IsEmpty(const RECT& rect) -{ - return rect.left == rect.right || - rect.top == rect.bottom; -} - gl::Error Blit9::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect, IDirect3DTexture9 **outTexture) { ASSERT(surface); @@ -523,10 +516,7 @@ gl::Error Blit9::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &so // Copy the render target into a texture IDirect3DTexture9 *texture; - HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, - sourceRect.bottom - sourceRect.top, 1, - D3DUSAGE_RENDERTARGET, sourceDesc.Format, - D3DPOOL_DEFAULT, &texture, NULL); + HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.bottom - sourceRect.top, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL); if (FAILED(result)) { @@ -544,82 +534,13 @@ gl::Error Blit9::copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &so return gl::Error(GL_OUT_OF_MEMORY, "Failed to query surface of internal blit texture, result: 0x%X.", result); } - D3DSURFACE_DESC destDesc; - textureSurface->GetDesc(&destDesc); - - RECT subsetSourceRect = sourceRect; - RECT subsetDestRect = { 0, 0, destDesc.Width, destDesc.Height }; - if (sourceRect.left < 0) { - LONG diff = 0 - sourceRect.left; - ASSERT(diff > 0); - subsetSourceRect.left += diff; - subsetDestRect.left += diff; - } - - if (sourceRect.top < 0) { - LONG diff = 0 - sourceRect.top; - ASSERT(diff > 0); - subsetSourceRect.top += diff; - subsetDestRect.top += diff; - } - - if (sourceRect.right > LONG(sourceDesc.Width)) { - LONG diff = sourceRect.right - sourceDesc.Width; - ASSERT(diff > 0); - subsetSourceRect.right -= diff; - subsetDestRect.right -= diff; - } - - if (sourceRect.bottom > LONG(sourceDesc.Height)) { - LONG diff = sourceRect.bottom - sourceDesc.Height; - ASSERT(diff > 0); - subsetSourceRect.bottom -= diff; - subsetDestRect.bottom -= diff; - } - - ASSERT(subsetSourceRect.left <= subsetSourceRect.right); - ASSERT(subsetSourceRect.top <= subsetSourceRect.bottom); - ASSERT(subsetDestRect.left <= subsetDestRect.right); - ASSERT(subsetDestRect.top <= subsetDestRect.bottom); - - ASSERT(subsetSourceRect.right - subsetSourceRect.left == subsetDestRect.right - subsetDestRect.left); - ASSERT(subsetSourceRect.bottom - subsetSourceRect.top == subsetDestRect.bottom - subsetDestRect.top); - mRenderer->endScene(); - - if (!IsEmpty(subsetSourceRect)) { - result = device->StretchRect(surface, &subsetSourceRect, textureSurface, &subsetDestRect, D3DTEXF_NONE); - } + result = device->StretchRect(surface, &sourceRect, textureSurface, NULL, D3DTEXF_NONE); SafeRelease(textureSurface); if (FAILED(result)) { - if (!(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)) { - std::printf("Bad result: 0x%x\n", result); - - std::printf("sourceRect: %i,%i - %i,%i\n", sourceRect.left, sourceRect.top, - sourceRect.right, sourceRect.bottom); - - std::printf("sourceDesc: Format: %u, Type: %u, Usage: %u, Pool: %u," - " MSType: %u, MSQuality: %u, Width: %u, Height: %u\n", - sourceDesc.Format, sourceDesc.Type, sourceDesc.Usage, - sourceDesc.Pool, sourceDesc.MultiSampleType, - sourceDesc.MultiSampleQuality, sourceDesc.Width, - sourceDesc.Height); - - std::printf("subsetSourceRect: %i,%i - %i,%i\n", subsetSourceRect.left, subsetSourceRect.top, - subsetSourceRect.right, subsetSourceRect.bottom); - - std::printf("destDesc: Format: %u, Type: %u, Usage: %u, Pool: %u, MSType: %u," - " MSQuality: %u, Width: %u, Height: %u\n", - destDesc.Format, destDesc.Type, destDesc.Usage, destDesc.Pool, - destDesc.MultiSampleType, destDesc.MultiSampleQuality, - destDesc.Width, destDesc.Height); - - std::printf("subsetDestRect: %i,%i - %i,%i\n", subsetDestRect.left, subsetDestRect.top, - subsetDestRect.right, subsetDestRect.bottom); - } ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); SafeRelease(texture); return gl::Error(GL_OUT_OF_MEMORY, "Failed to copy between internal blit textures, result: 0x%X.", result); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp index 69d3cf5e0d94..804b6971ced3 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp @@ -38,7 +38,7 @@ gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage) memcpy(mMemory.data(), data, size); } - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); updateD3DBufferUsage(usage); return gl::Error(GL_NO_ERROR); @@ -66,7 +66,7 @@ gl::Error Buffer9::setSubData(const void* data, size_t size, size_t offset) memcpy(mMemory.data() + offset, data, size); } - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); return gl::Error(GL_NO_ERROR); } @@ -79,7 +79,7 @@ gl::Error Buffer9::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintp memcpy(mMemory.data() + destOffset, sourceBuffer->mMemory.data() + sourceOffset, size); - invalidateStaticData(); + invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); return gl::Error(GL_NO_ERROR); } diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp index 55773e765cb0..d6d0b78195cd 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp @@ -8,19 +8,35 @@ #include "libANGLE/renderer/d3d/d3d9/Renderer9.h" +#include +#include + #include "common/utilities.h" +#include "libANGLE/angletypes.h" #include "libANGLE/Buffer.h" #include "libANGLE/Display.h" +#include "libANGLE/features.h" +#include "libANGLE/formatutils.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/Program.h" #include "libANGLE/Renderbuffer.h" -#include "libANGLE/State.h" -#include "libANGLE/Surface.h" -#include "libANGLE/Texture.h" -#include "libANGLE/angletypes.h" -#include "libANGLE/features.h" -#include "libANGLE/formatutils.h" +#include "libANGLE/renderer/d3d/d3d9/Blit9.h" +#include "libANGLE/renderer/d3d/d3d9/Buffer9.h" +#include "libANGLE/renderer/d3d/d3d9/Fence9.h" +#include "libANGLE/renderer/d3d/d3d9/formatutils9.h" +#include "libANGLE/renderer/d3d/d3d9/Framebuffer9.h" +#include "libANGLE/renderer/d3d/d3d9/Image9.h" +#include "libANGLE/renderer/d3d/d3d9/IndexBuffer9.h" +#include "libANGLE/renderer/d3d/d3d9/Query9.h" +#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" +#include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h" +#include "libANGLE/renderer/d3d/d3d9/ShaderExecutable9.h" +#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h" +#include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" +#include "libANGLE/renderer/d3d/d3d9/VertexArray9.h" +#include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h" +#include "libANGLE/renderer/d3d/DeviceD3D.h" #include "libANGLE/renderer/d3d/FramebufferD3D.h" #include "libANGLE/renderer/d3d/IndexDataManager.h" #include "libANGLE/renderer/d3d/ProgramD3D.h" @@ -29,28 +45,12 @@ #include "libANGLE/renderer/d3d/SurfaceD3D.h" #include "libANGLE/renderer/d3d/TextureD3D.h" #include "libANGLE/renderer/d3d/TransformFeedbackD3D.h" -#include "libANGLE/renderer/d3d/d3d9/Blit9.h" -#include "libANGLE/renderer/d3d/d3d9/Buffer9.h" -#include "libANGLE/renderer/d3d/d3d9/Fence9.h" -#include "libANGLE/renderer/d3d/d3d9/Framebuffer9.h" -#include "libANGLE/renderer/d3d/d3d9/Image9.h" -#include "libANGLE/renderer/d3d/d3d9/IndexBuffer9.h" -#include "libANGLE/renderer/d3d/d3d9/Query9.h" -#include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h" -#include "libANGLE/renderer/d3d/d3d9/ShaderExecutable9.h" -#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h" -#include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" -#include "libANGLE/renderer/d3d/d3d9/VertexArray9.h" -#include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h" -#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" -#include "libANGLE/renderer/d3d/d3d9/formatutils9.h" - +#include "libANGLE/State.h" +#include "libANGLE/Surface.h" +#include "libANGLE/Texture.h" #include "third_party/trace_event/trace_event.h" -#include -#include -#include #if !defined(ANGLE_COMPILE_OPTIMIZATION_LEVEL) #define ANGLE_COMPILE_OPTIMIZATION_LEVEL D3DCOMPILE_OPTIMIZATION_LEVEL3 @@ -923,7 +923,9 @@ gl::Error Renderer9::setRasterizerState(const gl::RasterizerState &rasterState) return gl::Error(GL_NO_ERROR); } -gl::Error Renderer9::setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, +gl::Error Renderer9::setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, unsigned int sampleMask) { bool blendStateChanged = mForceSetBlendState || memcmp(&blendState, &mCurBlendState, sizeof(gl::BlendState)) != 0; @@ -1065,13 +1067,15 @@ gl::Error Renderer9::setDepthStencilState(const gl::DepthStencilState &depthSten const D3DRENDERSTATETYPE D3DRS_CCW_STENCILMASK = D3DRS_STENCILMASK; const D3DRENDERSTATETYPE D3DRS_CCW_STENCILWRITEMASK = D3DRS_STENCILWRITEMASK; - ASSERT(depthStencilState.stencilWritemask == depthStencilState.stencilBackWritemask); - ASSERT(stencilRef == stencilBackRef); - ASSERT(depthStencilState.stencilMask == depthStencilState.stencilBackMask); - // get the maximum size of the stencil ref unsigned int maxStencil = (1 << mCurStencilSize) - 1; + ASSERT((depthStencilState.stencilWritemask & maxStencil) == + (depthStencilState.stencilBackWritemask & maxStencil)); + ASSERT(stencilRef == stencilBackRef); + ASSERT((depthStencilState.stencilMask & maxStencil) == + (depthStencilState.stencilBackMask & maxStencil)); + mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILWRITEMASK : D3DRS_CCW_STENCILWRITEMASK, depthStencilState.stencilWritemask); mDevice->SetRenderState(frontFaceCCW ? D3DRS_STENCILFUNC : D3DRS_CCW_STENCILFUNC, @@ -1453,9 +1457,18 @@ gl::Error Renderer9::applyVertexBuffer(const gl::State &state, GLenum mode, GLin } // Applies the indices and element array bindings to the Direct3D 9 device -gl::Error Renderer9::applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo) +gl::Error Renderer9::applyIndexBuffer(const gl::Data &data, + const GLvoid *indices, + GLsizei count, + GLenum mode, + GLenum type, + TranslatedIndexData *indexInfo, + SourceIndexData *sourceIndexInfo) { - gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, indexInfo, sourceIndexInfo); + gl::VertexArray *vao = data.state->getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + gl::Error error = mIndexDataManager->prepareIndexData(type, count, elementArrayBuffer, indices, + indexInfo, sourceIndexInfo, false); if (error.isError()) { return error; @@ -1483,8 +1496,7 @@ void Renderer9::applyTransformFeedbackBuffers(const gl::State& state) gl::Error Renderer9::drawArraysImpl(const gl::Data &data, GLenum mode, GLsizei count, - GLsizei instances, - bool usesPointSize) + GLsizei instances) { ASSERT(!data.state->isTransformFeedbackActiveUnpaused()); @@ -1525,19 +1537,21 @@ gl::Error Renderer9::drawArraysImpl(const gl::Data &data, } } -gl::Error Renderer9::drawElementsImpl(GLenum mode, +gl::Error Renderer9::drawElementsImpl(const gl::Data &data, + const TranslatedIndexData &indexInfo, + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, - gl::Buffer *elementArrayBuffer, - const TranslatedIndexData &indexInfo, - GLsizei /*instances*/, - bool /*usesPointSize*/) + GLsizei /*instances*/) { startScene(); int minIndex = static_cast(indexInfo.indexRange.start); + gl::VertexArray *vao = data.state->getVertexArray(); + gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); + if (mode == GL_POINTS) { return drawIndexedPoints(count, type, indices, minIndex, elementArrayBuffer); @@ -1860,15 +1874,9 @@ gl::Error Renderer9::getCountingIB(size_t count, StaticIndexBufferInterface **ou return gl::Error(GL_NO_ERROR); } -gl::Error Renderer9::applyShaders(gl::Program *program, - const gl::Framebuffer *framebuffer, - bool rasterizerDiscard, - bool transformFeedbackActive) +gl::Error Renderer9::applyShadersImpl(const gl::Data &data, GLenum /*drawMode*/) { - ASSERT(!transformFeedbackActive); - ASSERT(!rasterizerDiscard); - - ProgramD3D *programD3D = GetImplAs(program); + ProgramD3D *programD3D = GetImplAs(data.state->getProgram()); const auto &inputLayout = programD3D->getCachedInputLayout(); ShaderExecutableD3D *vertexExe = NULL; @@ -1878,8 +1886,9 @@ gl::Error Renderer9::applyShaders(gl::Program *program, return error; } + const gl::Framebuffer *drawFramebuffer = data.state->getDrawFramebuffer(); ShaderExecutableD3D *pixelExe = NULL; - error = programD3D->getPixelExecutableForFramebuffer(framebuffer, &pixelExe); + error = programD3D->getPixelExecutableForFramebuffer(drawFramebuffer, &pixelExe); if (error.isError()) { return error; @@ -1917,6 +1926,7 @@ gl::Error Renderer9::applyShaders(gl::Program *program, } gl::Error Renderer9::applyUniforms(const ProgramD3D &programD3D, + GLenum /*drawMode*/, const std::vector &uniformArray) { for (const D3DUniform *targetUniform : uniformArray) @@ -2723,12 +2733,15 @@ ProgramImpl *Renderer9::createProgram(const gl::Program::Data &data) return new ProgramD3D(data, this); } -gl::Error Renderer9::loadExecutable(const void *function, size_t length, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, ShaderExecutableD3D **outExecutable) +gl::Error Renderer9::loadExecutable(const void *function, + size_t length, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) { // Transform feedback is not supported in ES2 or D3D9 - ASSERT(transformFeedbackVaryings.size() == 0); + ASSERT(streamOutVaryings.empty()); switch (type) { @@ -2762,13 +2775,16 @@ gl::Error Renderer9::loadExecutable(const void *function, size_t length, ShaderT return gl::Error(GL_NO_ERROR); } -gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog, const std::string &shaderHLSL, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, const D3DCompilerWorkarounds &workarounds, +gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog, + const std::string &shaderHLSL, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + const D3DCompilerWorkarounds &workarounds, ShaderExecutableD3D **outExectuable) { // Transform feedback is not supported in ES2 or D3D9 - ASSERT(transformFeedbackVaryings.size() == 0); + ASSERT(streamOutVaryings.empty()); const char *profileType = NULL; switch (type) @@ -2831,7 +2847,7 @@ gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog, const std::string } error = loadExecutable(binary->GetBufferPointer(), binary->GetBufferSize(), type, - transformFeedbackVaryings, separatedOutputBuffers, outExectuable); + streamOutVaryings, separatedOutputBuffers, outExectuable); SafeRelease(binary); if (error.isError()) @@ -3036,6 +3052,17 @@ gl::Error Renderer9::clearTextures(gl::SamplerType samplerType, size_t rangeStar return gl::Error(GL_NO_ERROR); } +egl::Error Renderer9::initializeEGLDevice(DeviceD3D **outDevice) +{ + if (*outDevice == nullptr) + { + ASSERT(mDevice != nullptr); + *outDevice = new DeviceD3D(reinterpret_cast(mDevice), EGL_D3D9_DEVICE_ANGLE); + } + + return egl::Error(EGL_SUCCESS); +} + Renderer9::CurSamplerState::CurSamplerState() : forceSet(true), baseLevel(std::numeric_limits::max()), diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h index e7d8a3f1af91..184523889104 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h @@ -97,7 +97,9 @@ class Renderer9 : public RendererD3D const std::vector &fragmentUniformBuffers) override; virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState); - gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor, + gl::Error setBlendState(const gl::Framebuffer *framebuffer, + const gl::BlendState &blendState, + const gl::ColorF &blendColor, unsigned int sampleMask) override; virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef, int stencilBackRef, bool frontFaceCCW); @@ -109,15 +111,18 @@ class Renderer9 : public RendererD3D gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override; gl::Error applyRenderTarget(const gl::FramebufferAttachment *colorAttachment, const gl::FramebufferAttachment *depthStencilAttachment); - gl::Error applyShaders(gl::Program *program, - const gl::Framebuffer *framebuffer, - bool rasterizerDiscard, - bool transformFeedbackActive) override; gl::Error applyUniforms(const ProgramD3D &programD3D, + GLenum drawMode, const std::vector &uniformArray) override; virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize); virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceInfo); - virtual gl::Error applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo, SourceIndexData *sourceIndexInfo); + gl::Error applyIndexBuffer(const gl::Data &data, + const GLvoid *indices, + GLsizei count, + GLenum mode, + GLenum type, + TranslatedIndexData *indexInfo, + SourceIndexData *sourceIndexInfo) override; void applyTransformFeedbackBuffers(const gl::State &state) override; @@ -173,14 +178,20 @@ class Renderer9 : public RendererD3D ProgramImpl *createProgram(const gl::Program::Data &data) override; // Shader operations - virtual gl::Error loadExecutable(const void *function, size_t length, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, ShaderExecutableD3D **outExecutable); - virtual gl::Error compileToExecutable(gl::InfoLog &infoLog, const std::string &shaderHLSL, ShaderType type, - const std::vector &transformFeedbackVaryings, - bool separatedOutputBuffers, const D3DCompilerWorkarounds &workarounds, - ShaderExecutableD3D **outExectuable); - virtual UniformStorageD3D *createUniformStorage(size_t storageSize); + gl::Error loadExecutable(const void *function, + size_t length, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + ShaderExecutableD3D **outExecutable) override; + gl::Error compileToExecutable(gl::InfoLog &infoLog, + const std::string &shaderHLSL, + ShaderType type, + const std::vector &streamOutVaryings, + bool separatedOutputBuffers, + const D3DCompilerWorkarounds &workarounds, + ShaderExecutableD3D **outExectuable) override; + UniformStorageD3D *createUniformStorage(size_t storageSize) override; // Image operations virtual ImageD3D *createImage(); @@ -221,6 +232,11 @@ class Renderer9 : public RendererD3D virtual gl::Error fastCopyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget, GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea); + void syncState(const gl::State & /*state*/, const gl::State::DirtyBits &bitmask) override + { + // TODO(dianx) implement d3d9 dirty bits + } + // D3D9-renderer specific methods gl::Error boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest); @@ -239,21 +255,22 @@ class Renderer9 : public RendererD3D protected: void createAnnotator() override; gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) override; + gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) override; + + egl::Error initializeEGLDevice(DeviceD3D **outDevice) override; private: gl::Error drawArraysImpl(const gl::Data &data, GLenum mode, GLsizei count, - GLsizei instances, - bool usesPointSize) override; - gl::Error drawElementsImpl(GLenum mode, + GLsizei instances) override; + gl::Error drawElementsImpl(const gl::Data &data, + const TranslatedIndexData &indexInfo, + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, - gl::Buffer *elementArrayBuffer, - const TranslatedIndexData &indexInfo, - GLsizei instances, - bool usesPointSize) override; + GLsizei instances) override; void generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, gl::Extensions *outExtensions, diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp index ed9c14639e28..b931035ef624 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp @@ -576,6 +576,7 @@ void GenerateCaps(IDirect3D9 *d3d9, extensions->eglImage = true; extensions->unpackSubimage = true; extensions->packSubimage = true; + extensions->vertexArrayObject = true; // D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil // state. diff --git a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp new file mode 100644 index 000000000000..97eb99299515 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.cpp @@ -0,0 +1,953 @@ +// +// Copyright (c) 2013-2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// loadimage_etc.cpp: Decodes ETC and EAC encoded textures. + +#include "libANGLE/renderer/d3d/loadimage_etc.h" + +#include "libANGLE/renderer/d3d/loadimage.h" +#include "libANGLE/renderer/d3d/imageformats.h" + +namespace rx +{ +namespace +{ + +struct ETC2Block +{ + // Decodes unsigned single or dual channel block to bytes + void decodeAsSingleChannel(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destPixelStride, + size_t destRowPitch, + bool isSigned) const + { + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + uint8_t *row = dest + (j * destRowPitch); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + uint8_t *pixel = row + (i * destPixelStride); + if (isSigned) + { + *pixel = clampSByte(getSingleChannel(i, j, isSigned)); + } + else + { + *pixel = clampByte(getSingleChannel(i, j, isSigned)); + } + } + } + } + + // Decodes RGB block to rgba8 + void decodeAsRGB(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + const uint8_t alphaValues[4][4], + bool punchThroughAlpha) const + { + bool opaqueBit = u.idht.mode.idm.diffbit; + bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit; + // Select mode + if (u.idht.mode.idm.diffbit || punchThroughAlpha) + { + const auto &block = u.idht.mode.idm.colors.diff; + int r = (block.R + block.dR); + int g = (block.G + block.dG); + int b = (block.B + block.dB); + if (r < 0 || r > 31) + { + decodeTBlock(dest, x, y, w, h, destRowPitch, alphaValues, + nonOpaquePunchThroughAlpha); + } + else if (g < 0 || g > 31) + { + decodeHBlock(dest, x, y, w, h, destRowPitch, alphaValues, + nonOpaquePunchThroughAlpha); + } + else if (b < 0 || b > 31) + { + decodePlanarBlock(dest, x, y, w, h, destRowPitch, alphaValues); + } + else + { + decodeDifferentialBlock(dest, x, y, w, h, destRowPitch, alphaValues, + nonOpaquePunchThroughAlpha); + } + } + else + { + decodeIndividualBlock(dest, x, y, w, h, destRowPitch, alphaValues, + nonOpaquePunchThroughAlpha); + } + } + + private: + union + { + // Individual, differential, H and T modes + struct + { + union + { + // Individual and differential modes + struct + { + union + { + struct // Individual colors + { + unsigned char R2 : 4; + unsigned char R1 : 4; + unsigned char G2 : 4; + unsigned char G1 : 4; + unsigned char B2 : 4; + unsigned char B1 : 4; + } indiv; + struct // Differential colors + { + signed char dR : 3; + unsigned char R : 5; + signed char dG : 3; + unsigned char G : 5; + signed char dB : 3; + unsigned char B : 5; + } diff; + } colors; + bool flipbit : 1; + bool diffbit : 1; + unsigned char cw2 : 3; + unsigned char cw1 : 3; + } idm; + // T mode + struct + { + // Byte 1 + unsigned char TR1b : 2; + unsigned char TdummyB : 1; + unsigned char TR1a : 2; + unsigned char TdummyA : 3; + // Byte 2 + unsigned char TB1 : 4; + unsigned char TG1 : 4; + // Byte 3 + unsigned char TG2 : 4; + unsigned char TR2 : 4; + // Byte 4 + unsigned char Tdb : 1; + bool Tflipbit : 1; + unsigned char Tda : 2; + unsigned char TB2 : 4; + } tm; + // H mode + struct + { + // Byte 1 + unsigned char HG1a : 3; + unsigned char HR1 : 4; + unsigned char HdummyA : 1; + // Byte 2 + unsigned char HB1b : 2; + unsigned char HdummyC : 1; + unsigned char HB1a : 1; + unsigned char HG1b : 1; + unsigned char HdummyB : 3; + // Byte 3 + unsigned char HG2a : 3; + unsigned char HR2 : 4; + unsigned char HB1c : 1; + // Byte 4 + unsigned char Hdb : 1; + bool Hflipbit : 1; + unsigned char Hda : 1; + unsigned char HB2 : 4; + unsigned char HG2b : 1; + } hm; + } mode; + unsigned char pixelIndexMSB[2]; + unsigned char pixelIndexLSB[2]; + } idht; + // planar mode + struct + { + // Byte 1 + unsigned char GO1 : 1; + unsigned char RO : 6; + unsigned char PdummyA : 1; + // Byte 2 + unsigned char BO1 : 1; + unsigned char GO2 : 6; + unsigned char PdummyB : 1; + // Byte 3 + unsigned char BO3a : 2; + unsigned char PdummyD : 1; + unsigned char BO2 : 2; + unsigned char PdummyC : 3; + // Byte 4 + unsigned char RH2 : 1; + bool Pflipbit : 1; + unsigned char RH1 : 5; + unsigned char BO3b : 1; + // Byte 5 + unsigned char BHa : 1; + unsigned char GH : 7; + // Byte 6 + unsigned char RVa : 3; + unsigned char BHb : 5; + // Byte 7 + unsigned char GVa : 5; + unsigned char RVb : 3; + // Byte 8 + unsigned char BV : 6; + unsigned char GVb : 2; + } pblk; + // Single channel block + struct + { + union + { + unsigned char us; + signed char s; + } base_codeword; + unsigned char table_index : 4; + unsigned char multiplier : 4; + unsigned char mc1 : 2; + unsigned char mb : 3; + unsigned char ma : 3; + unsigned char mf1 : 1; + unsigned char me : 3; + unsigned char md : 3; + unsigned char mc2 : 1; + unsigned char mh : 3; + unsigned char mg : 3; + unsigned char mf2 : 2; + unsigned char mk1 : 2; + unsigned char mj : 3; + unsigned char mi : 3; + unsigned char mn1 : 1; + unsigned char mm : 3; + unsigned char ml : 3; + unsigned char mk2 : 1; + unsigned char mp : 3; + unsigned char mo : 3; + unsigned char mn2 : 2; + } scblk; + } u; + + static unsigned char clampByte(int value) + { + return static_cast(gl::clamp(value, 0, 255)); + } + + static signed char clampSByte(int value) + { + return static_cast(gl::clamp(value, -128, 127)); + } + + static R8G8B8A8 createRGBA(int red, int green, int blue, int alpha) + { + R8G8B8A8 rgba; + rgba.R = clampByte(red); + rgba.G = clampByte(green); + rgba.B = clampByte(blue); + rgba.A = clampByte(alpha); + return rgba; + } + + static R8G8B8A8 createRGBA(int red, int green, int blue) + { + return createRGBA(red, green, blue, 255); + } + + static int extend_4to8bits(int x) { return (x << 4) | x; } + static int extend_5to8bits(int x) { return (x << 3) | (x >> 2); } + static int extend_6to8bits(int x) { return (x << 2) | (x >> 4); } + static int extend_7to8bits(int x) { return (x << 1) | (x >> 6); } + + void decodeIndividualBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + const auto &block = u.idht.mode.idm.colors.indiv; + int r1 = extend_4to8bits(block.R1); + int g1 = extend_4to8bits(block.G1); + int b1 = extend_4to8bits(block.B1); + int r2 = extend_4to8bits(block.R2); + int g2 = extend_4to8bits(block.G2); + int b2 = extend_4to8bits(block.B2); + decodeIndividualOrDifferentialBlock(dest, x, y, w, h, destRowPitch, r1, g1, b1, r2, g2, b2, + alphaValues, nonOpaquePunchThroughAlpha); + } + + void decodeDifferentialBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + const auto &block = u.idht.mode.idm.colors.diff; + int b1 = extend_5to8bits(block.B); + int g1 = extend_5to8bits(block.G); + int r1 = extend_5to8bits(block.R); + int r2 = extend_5to8bits(block.R + block.dR); + int g2 = extend_5to8bits(block.G + block.dG); + int b2 = extend_5to8bits(block.B + block.dB); + decodeIndividualOrDifferentialBlock(dest, x, y, w, h, destRowPitch, r1, g1, b1, r2, g2, b2, + alphaValues, nonOpaquePunchThroughAlpha); + } + + void decodeIndividualOrDifferentialBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + int r1, + int g1, + int b1, + int r2, + int g2, + int b2, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // Table 3.17.2 sorted according to table 3.17.3 + // clang-format off + static const int intensityModifierDefault[8][4] = + { + { 2, 8, -2, -8 }, + { 5, 17, -5, -17 }, + { 9, 29, -9, -29 }, + { 13, 42, -13, -42 }, + { 18, 60, -18, -60 }, + { 24, 80, -24, -80 }, + { 33, 106, -33, -106 }, + { 47, 183, -47, -183 }, + }; + // clang-format on + + // Table C.12, intensity modifier for non opaque punchthrough alpha + // clang-format off + static const int intensityModifierNonOpaque[8][4] = + { + { 0, 8, 0, -8 }, + { 0, 17, 0, -17 }, + { 0, 29, 0, -29 }, + { 0, 42, 0, -42 }, + { 0, 60, 0, -60 }, + { 0, 80, 0, -80 }, + { 0, 106, 0, -106 }, + { 0, 183, 0, -183 }, + }; + // clang-format on + + const int(&intensityModifier)[8][4] = + nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault; + + R8G8B8A8 subblockColors0[4]; + R8G8B8A8 subblockColors1[4]; + for (size_t blockIdx = 0; blockIdx < 4; blockIdx++) + { + const int i1 = intensityModifier[u.idht.mode.idm.cw1][blockIdx]; + subblockColors0[blockIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1); + + const int i2 = intensityModifier[u.idht.mode.idm.cw2][blockIdx]; + subblockColors1[blockIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2); + } + + if (u.idht.mode.idm.flipbit) + { + uint8_t *curPixel = dest; + for (size_t j = 0; j < 2 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + row[i] = subblockColors0[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + curPixel += destRowPitch; + } + for (size_t j = 2; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + row[i] = subblockColors1[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + curPixel += destRowPitch; + } + } + else + { + uint8_t *curPixel = dest; + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 2 && (x + i) < w; i++) + { + row[i] = subblockColors0[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + for (size_t i = 2; i < 4 && (x + i) < w; i++) + { + row[i] = subblockColors1[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + curPixel += destRowPitch; + } + } + if (nonOpaquePunchThroughAlpha) + { + decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch); + } + } + + void decodeTBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // Table C.8, distance index for T and H modes + const auto &block = u.idht.mode.tm; + + int r1 = extend_4to8bits(block.TR1a << 2 | block.TR1b); + int g1 = extend_4to8bits(block.TG1); + int b1 = extend_4to8bits(block.TB1); + int r2 = extend_4to8bits(block.TR2); + int g2 = extend_4to8bits(block.TG2); + int b2 = extend_4to8bits(block.TB2); + + static int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64}; + const int d = distance[block.Tda << 1 | block.Tdb]; + + const R8G8B8A8 paintColors[4] = { + createRGBA(r1, g1, b1), createRGBA(r2 + d, g2 + d, b2 + d), createRGBA(r2, g2, b2), + createRGBA(r2 - d, g2 - d, b2 - d), + }; + + uint8_t *curPixel = dest; + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + row[i] = paintColors[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + curPixel += destRowPitch; + } + + if (nonOpaquePunchThroughAlpha) + { + decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch); + } + } + + void decodeHBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch, + const uint8_t alphaValues[4][4], + bool nonOpaquePunchThroughAlpha) const + { + // Table C.8, distance index for T and H modes + const auto &block = u.idht.mode.hm; + + int r1 = extend_4to8bits(block.HR1); + int g1 = extend_4to8bits(block.HG1a << 1 | block.HG1b); + int b1 = extend_4to8bits(block.HB1a << 3 | block.HB1b << 1 | block.HB1c); + int r2 = extend_4to8bits(block.HR2); + int g2 = extend_4to8bits(block.HG2a << 1 | block.HG2b); + int b2 = extend_4to8bits(block.HB2); + + static const int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64}; + const int d = distance[(block.Hda << 2) | (block.Hdb << 1) | + ((r1 << 16 | g1 << 8 | b1) >= (r2 << 16 | g2 << 8 | b2) ? 1 : 0)]; + + const R8G8B8A8 paintColors[4] = { + createRGBA(r1 + d, g1 + d, b1 + d), createRGBA(r1 - d, g1 - d, b1 - d), + createRGBA(r2 + d, g2 + d, b2 + d), createRGBA(r2 - d, g2 - d, b2 - d), + }; + + uint8_t *curPixel = dest; + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + row[i] = paintColors[getIndex(i, j)]; + row[i].A = alphaValues[j][i]; + } + curPixel += destRowPitch; + } + + if (nonOpaquePunchThroughAlpha) + { + decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch); + } + } + + void decodePlanarBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t pitch, + const uint8_t alphaValues[4][4]) const + { + int ro = extend_6to8bits(u.pblk.RO); + int go = extend_7to8bits(u.pblk.GO1 << 6 | u.pblk.GO2); + int bo = + extend_6to8bits(u.pblk.BO1 << 5 | u.pblk.BO2 << 3 | u.pblk.BO3a << 1 | u.pblk.BO3b); + int rh = extend_6to8bits(u.pblk.RH1 << 1 | u.pblk.RH2); + int gh = extend_7to8bits(u.pblk.GH); + int bh = extend_6to8bits(u.pblk.BHa << 5 | u.pblk.BHb); + int rv = extend_6to8bits(u.pblk.RVa << 3 | u.pblk.RVb); + int gv = extend_7to8bits(u.pblk.GVa << 2 | u.pblk.GVb); + int bv = extend_6to8bits(u.pblk.BV); + + uint8_t *curPixel = dest; + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + + int ry = static_cast(j) * (rv - ro) + 2; + int gy = static_cast(j) * (gv - go) + 2; + int by = static_cast(j) * (bv - bo) + 2; + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + row[i] = createRGBA(((static_cast(i) * (rh - ro) + ry) >> 2) + ro, + ((static_cast(i) * (gh - go) + gy) >> 2) + go, + ((static_cast(i) * (bh - bo) + by) >> 2) + bo, + alphaValues[j][i]); + } + curPixel += pitch; + } + } + + // Index for individual, differential, H and T modes + size_t getIndex(size_t x, size_t y) const + { + size_t bitIndex = x * 4 + y; + size_t bitOffset = bitIndex & 7; + size_t lsb = (u.idht.pixelIndexLSB[1 - (bitIndex >> 3)] >> bitOffset) & 1; + size_t msb = (u.idht.pixelIndexMSB[1 - (bitIndex >> 3)] >> bitOffset) & 1; + return (msb << 1) | lsb; + } + + void decodePunchThroughAlphaBlock(uint8_t *dest, + size_t x, + size_t y, + size_t w, + size_t h, + size_t destRowPitch) const + { + uint8_t *curPixel = dest; + for (size_t j = 0; j < 4 && (y + j) < h; j++) + { + R8G8B8A8 *row = reinterpret_cast(curPixel); + for (size_t i = 0; i < 4 && (x + i) < w; i++) + { + if (getIndex(i, j) == 2) // msb == 1 && lsb == 0 + { + row[i] = createRGBA(0, 0, 0, 0); + } + } + curPixel += destRowPitch; + } + } + + // Single channel utility functions + int getSingleChannel(size_t x, size_t y, bool isSigned) const + { + int codeword = isSigned ? u.scblk.base_codeword.s : u.scblk.base_codeword.us; + return codeword + getSingleChannelModifier(x, y) * u.scblk.multiplier; + } + + int getSingleChannelIndex(size_t x, size_t y) const + { + ASSERT(x < 4 && y < 4); + + // clang-format off + switch (x * 4 + y) + { + case 0: return u.scblk.ma; + case 1: return u.scblk.mb; + case 2: return u.scblk.mc1 << 1 | u.scblk.mc2; + case 3: return u.scblk.md; + case 4: return u.scblk.me; + case 5: return u.scblk.mf1 << 2 | u.scblk.mf2; + case 6: return u.scblk.mg; + case 7: return u.scblk.mh; + case 8: return u.scblk.mi; + case 9: return u.scblk.mj; + case 10: return u.scblk.mk1 << 1 | u.scblk.mk2; + case 11: return u.scblk.ml; + case 12: return u.scblk.mm; + case 13: return u.scblk.mn1 << 2 | u.scblk.mn2; + case 14: return u.scblk.mo; + case 15: return u.scblk.mp; + default: UNREACHABLE(); return 0; + } + // clang-format on + } + + int getSingleChannelModifier(size_t x, size_t y) const + { + // clang-format off + static const int modifierTable[16][8] = + { + { -3, -6, -9, -15, 2, 5, 8, 14 }, + { -3, -7, -10, -13, 2, 6, 9, 12 }, + { -2, -5, -8, -13, 1, 4, 7, 12 }, + { -2, -4, -6, -13, 1, 3, 5, 12 }, + { -3, -6, -8, -12, 2, 5, 7, 11 }, + { -3, -7, -9, -11, 2, 6, 8, 10 }, + { -4, -7, -8, -11, 3, 6, 7, 10 }, + { -3, -5, -8, -11, 2, 4, 7, 10 }, + { -2, -6, -8, -10, 1, 5, 7, 9 }, + { -2, -5, -8, -10, 1, 4, 7, 9 }, + { -2, -4, -8, -10, 1, 3, 7, 9 }, + { -2, -5, -7, -10, 1, 4, 6, 9 }, + { -3, -4, -7, -10, 2, 3, 6, 9 }, + { -1, -2, -3, -10, 0, 1, 2, 9 }, + { -4, -6, -8, -9, 3, 5, 7, 8 }, + { -3, -5, -7, -9, 2, 4, 6, 8 } + }; + // clang-format on + + return modifierTable[u.scblk.table_index][getSingleChannelIndex(x, y)]; + } +}; + +// clang-format off +static const uint8_t DefaultETCAlphaValues[4][4] = +{ + { 255, 255, 255, 255 }, + { 255, 255, 255, 255 }, + { 255, 255, 255, 255 }, + { 255, 255, 255, 255 }, +}; +// clang-format on + +void LoadR11EACToR8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch, + bool isSigned) +{ + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y += 4) + { + const ETC2Block *sourceRow = + OffsetDataPointer(input, y / 4, z, inputRowPitch, inputDepthPitch); + uint8_t *destRow = + OffsetDataPointer(output, y, z, outputRowPitch, outputDepthPitch); + + for (size_t x = 0; x < width; x += 4) + { + const ETC2Block *sourceBlock = sourceRow + (x / 4); + uint8_t *destPixels = destRow + x; + + sourceBlock->decodeAsSingleChannel(destPixels, x, y, width, height, 1, + outputRowPitch, isSigned); + } + } + } +} + +void LoadRG11EACToRG8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch, + bool isSigned) +{ + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y += 4) + { + const ETC2Block *sourceRow = + OffsetDataPointer(input, y / 4, z, inputRowPitch, inputDepthPitch); + uint8_t *destRow = + OffsetDataPointer(output, y, z, outputRowPitch, outputDepthPitch); + + for (size_t x = 0; x < width; x += 4) + { + uint8_t *destPixelsRed = destRow + (x * 2); + const ETC2Block *sourceBlockRed = sourceRow + (x / 2); + sourceBlockRed->decodeAsSingleChannel(destPixelsRed, x, y, width, height, 2, + outputRowPitch, isSigned); + + uint8_t *destPixelsGreen = destPixelsRed + 1; + const ETC2Block *sourceBlockGreen = sourceBlockRed + 1; + sourceBlockGreen->decodeAsSingleChannel(destPixelsGreen, x, y, width, height, 2, + outputRowPitch, isSigned); + } + } + } +} + +void LoadETC2RGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch, + bool punchthroughAlpha) +{ + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y += 4) + { + const ETC2Block *sourceRow = + OffsetDataPointer(input, y / 4, z, inputRowPitch, inputDepthPitch); + uint8_t *destRow = + OffsetDataPointer(output, y, z, outputRowPitch, outputDepthPitch); + + for (size_t x = 0; x < width; x += 4) + { + const ETC2Block *sourceBlock = sourceRow + (x / 4); + uint8_t *destPixels = destRow + (x * 4); + + sourceBlock->decodeAsRGB(destPixels, x, y, width, height, outputRowPitch, + DefaultETCAlphaValues, punchthroughAlpha); + } + } + } +} + +void LoadETC2RGBA8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch, + bool srgb) +{ + uint8_t decodedAlphaValues[4][4]; + + for (size_t z = 0; z < depth; z++) + { + for (size_t y = 0; y < height; y += 4) + { + const ETC2Block *sourceRow = + OffsetDataPointer(input, y / 4, z, inputRowPitch, inputDepthPitch); + uint8_t *destRow = + OffsetDataPointer(output, y, z, outputRowPitch, outputDepthPitch); + + for (size_t x = 0; x < width; x += 4) + { + const ETC2Block *sourceBlockAlpha = sourceRow + (x / 2); + sourceBlockAlpha->decodeAsSingleChannel( + reinterpret_cast(decodedAlphaValues), x, y, width, height, 1, 4, + false); + + uint8_t *destPixels = destRow + (x * 4); + const ETC2Block *sourceBlockRGB = sourceBlockAlpha + 1; + sourceBlockRGB->decodeAsRGB(destPixels, x, y, width, height, outputRowPitch, + decodedAlphaValues, false); + } + } + } +} + +} // anonymous namespace + +void LoadETC1RGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadEACR11ToR8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadR11EACToR8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadEACR11SToR8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadR11EACToR8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, true); +} + +void LoadEACRG11ToRG8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadRG11EACToRG8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadEACRG11SToRG8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadRG11EACToRG8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, true); +} + +void LoadETC2RGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadETC2SRGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadETC2RGB8A1ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, true); +} + +void LoadETC2SRGB8A1ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, true); +} + +void LoadETC2RGBA8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGBA8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, false); +} + +void LoadETC2SRGBA8ToSRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch) +{ + LoadETC2RGBA8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch, true); +} + +} // namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h new file mode 100644 index 000000000000..2ada743a2436 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/d3d/loadimage_etc.h @@ -0,0 +1,130 @@ +// +// Copyright (c) 2013-2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// loadimage_etc.h: Decodes ETC and EAC encoded textures. + +#ifndef LIBANGLE_RENDERER_D3D_LOADIMAGE_ETC_H_ +#define LIBANGLE_RENDERER_D3D_LOADIMAGE_ETC_H_ + +#include "libANGLE/angletypes.h" + +#include + +namespace rx +{ + +void LoadETC1RGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadEACR11ToR8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadEACR11SToR8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadEACRG11ToRG8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadEACRG11SToRG8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2RGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2SRGB8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2RGB8A1ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2SRGB8A1ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2RGBA8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadETC2SRGBA8ToSRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); +} + +#endif // LIBANGLE_RENDERER_D3D_LOADIMAGE_ETC_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/BlitGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/BlitGL.cpp index b6aa401fc536..0a624dcb17dd 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/BlitGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/BlitGL.cpp @@ -58,10 +58,14 @@ BlitGL::BlitGL(const FunctionsGL *functions, mWorkarounds(workarounds), mStateManager(stateManager), mBlitProgram(0), - mScratchTexture(0), mScratchFBO(0), mVAO(0) { + for (size_t i = 0; i < ArraySize(mScratchTextures); i++) + { + mScratchTextures[i] = 0; + } + ASSERT(mFunctions); ASSERT(mStateManager); } @@ -70,25 +74,28 @@ BlitGL::~BlitGL() { if (mBlitProgram != 0) { - mFunctions->deleteProgram(mBlitProgram); + mStateManager->deleteProgram(mBlitProgram); mBlitProgram = 0; } - if (mScratchTexture != 0) + for (size_t i = 0; i < ArraySize(mScratchTextures); i++) { - mFunctions->deleteTextures(1, &mScratchTexture); - mScratchTexture = 0; + if (mScratchTextures[i] != 0) + { + mStateManager->deleteTexture(mScratchTextures[i]); + mScratchTextures[i] = 0; + } } if (mScratchFBO != 0) { - mFunctions->deleteFramebuffers(1, &mScratchFBO); + mStateManager->deleteFramebuffer(mScratchFBO); mScratchFBO = 0; } if (mVAO != 0) { - mFunctions->deleteVertexArrays(1, &mVAO); + mStateManager->deleteVertexArray(mVAO); mVAO = 0; } } @@ -129,16 +136,18 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, return error; } - // Blit the framebuffer to the scratch texture + // Blit the framebuffer to the first scratch texture const FramebufferGL *sourceFramebufferGL = GetImplAs(source); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); nativegl::CopyTexImageImageFormat copyTexImageFormat = nativegl::GetCopyTexImageImageFormat( mFunctions, mWorkarounds, source->getImplementationColorReadFormat(), source->getImplementationColorReadType()); + const gl::InternalFormat &internalFormatInfo = + gl::GetInternalFormatInfo(copyTexImageFormat.internalFormat); mStateManager->activeTexture(0); - mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTexture); + mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]); mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat, sourceArea.x, sourceArea.y, sourceArea.width, sourceArea.height, 0); @@ -150,15 +159,20 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, }; mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); - // Make a temporary framebuffer using the destination texture + // Make a temporary framebuffer using the second scratch texture to render the swizzled result + // to. + mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[1]); + mFunctions->texImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat, sourceArea.width, + sourceArea.height, 0, internalFormatInfo.format, + source->getImplementationColorReadType(), nullptr); + mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO); - mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, texture, - static_cast(level)); + mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mScratchTextures[1], 0); // Render to the destination texture, sampling from the scratch texture mStateManager->useProgram(mBlitProgram); - mStateManager->setViewport( - gl::Rectangle(destOffset.x, destOffset.y, sourceArea.width, sourceArea.height)); + mStateManager->setViewport(gl::Rectangle(0, 0, sourceArea.width, sourceArea.height)); mStateManager->setScissorTestEnabled(false); mStateManager->setDepthRange(0.0f, 1.0f); mStateManager->setBlendEnabled(false); @@ -170,11 +184,17 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, mStateManager->setCullFaceEnabled(false); mStateManager->setPolygonOffsetFillEnabled(false); mStateManager->setRasterizerDiscardEnabled(false); + mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]); mStateManager->bindVertexArray(mVAO, 0); mFunctions->drawArrays(GL_TRIANGLES, 0, 6); + // Finally, copy the swizzled texture to the destination texture + mStateManager->bindTexture(textureType, texture); + mFunctions->copyTexSubImage2D(target, static_cast(level), destOffset.x, destOffset.y, 0, + 0, sourceArea.width, sourceArea.height); + return gl::Error(GL_NO_ERROR); } @@ -256,14 +276,17 @@ gl::Error BlitGL::initializeResources() mFunctions->uniform1i(textureUniform, 0); } - if (mScratchTexture == 0) + for (size_t i = 0; i < ArraySize(mScratchTextures); i++) { - mFunctions->genTextures(1, &mScratchTexture); - mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTexture); + if (mScratchTextures[i] == 0) + { + mFunctions->genTextures(1, &mScratchTextures[i]); + mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[i]); - // Use nearest, non-mipmapped sampling with the scratch texture - mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // Use nearest, non-mipmapped sampling with the scratch texture + mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } } if (mScratchFBO == 0) diff --git a/gfx/angle/src/libANGLE/renderer/gl/BlitGL.h b/gfx/angle/src/libANGLE/renderer/gl/BlitGL.h index 6d7d34f92561..3ab8319bb138 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/BlitGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/BlitGL.h @@ -61,7 +61,7 @@ class BlitGL : public angle::NonCopyable GLuint mBlitProgram; - GLuint mScratchTexture; + GLuint mScratchTextures[2]; GLuint mScratchFBO; GLuint mVAO; diff --git a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp index c7f772225217..37e13a4d3b1e 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp @@ -788,14 +788,11 @@ void FunctionsGL::initialize() } // Check the context profile + profile = 0; if (isAtLeastGL(gl::Version(3, 2))) { getIntegerv(GL_CONTEXT_PROFILE_MASK, &profile); } - else - { - profile = 0; - } // clang-format off @@ -918,6 +915,29 @@ void FunctionsGL::initialize() AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGetQueryObjectivARB"), &getQueryObjectiv); AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGetQueryObjectuivARB"), &getQueryObjectuiv); + // EXT_transform_feedback + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glBindBufferRangeEXT"), &bindBufferRange); + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glBindBufferBaseEXT"), &bindBufferBase); + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glBeginTransformFeedbackEXT"), &beginTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glEndTransformFeedbackEXT"), &endTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glTransformFeedbackVaryingsEXT"), &transformFeedbackVaryings); + AssignGLExtensionEntryPoint(extensions, "EXT_transform_feedback", loadProcAddress("glGetTransformFeedbackVaryingEXT"), &getTransformFeedbackVarying); + + // GL_ARB_transform_feedback2 + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glBindTransformFeedback"), &bindTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glDeleteTransformFeedbacks"), &deleteTransformFeedbacks); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glGenTransformFeedbacks"), &genTransformFeedbacks); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glIsTransformFeedback"), &isTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glPauseTransformFeedback"), &pauseTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glResumeTransformFeedback"), &resumeTransformFeedback); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback2", loadProcAddress("glDrawTransformFeedback"), &drawTransformFeedback); + + // GL_ARB_transform_feedback3 + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback3", loadProcAddress("glDrawTransformFeedbackStream"), &drawTransformFeedbackStream); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback3", loadProcAddress("glBeginQueryIndexed"), &beginQueryIndexed); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback3", loadProcAddress("glEndQueryIndexed"), &endQueryIndexed); + AssignGLExtensionEntryPoint(extensions, "GL_ARB_transform_feedback3", loadProcAddress("glGetQueryIndexediv"), &getQueryIndexediv); + // 1.0 if (isAtLeastGL(gl::Version(1, 0))) { diff --git a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp index b3e36b0dc3d0..80d8c92be639 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp @@ -51,6 +51,29 @@ LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog) // Reset the program state, delete the current program if one exists reset(); + // Set the transform feedback state + std::vector transformFeedbackVaryings; + for (const auto &tfVarying : mData.getTransformFeedbackVaryingNames()) + { + transformFeedbackVaryings.push_back(tfVarying.c_str()); + } + + if (transformFeedbackVaryings.empty()) + { + if (mFunctions->transformFeedbackVaryings) + { + mFunctions->transformFeedbackVaryings(mProgramID, 0, nullptr, + mData.getTransformFeedbackBufferMode()); + } + } + else + { + ASSERT(mFunctions->transformFeedbackVaryings); + mFunctions->transformFeedbackVaryings( + mProgramID, static_cast(transformFeedbackVaryings.size()), + &transformFeedbackVaryings[0], mData.getTransformFeedbackBufferMode()); + } + const gl::Shader *vertexShader = mData.getAttachedVertexShader(); const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); diff --git a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp index 0e10fd7bc8d4..d5e13258fcb0 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp @@ -119,8 +119,8 @@ RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &at RendererGL::~RendererGL() { - SafeDelete(mStateManager); SafeDelete(mBlitter); + SafeDelete(mStateManager); } gl::Error RendererGL::flush() @@ -315,7 +315,8 @@ FenceSyncImpl *RendererGL::createFenceSync() TransformFeedbackImpl *RendererGL::createTransformFeedback() { - return new TransformFeedbackGL(); + return new TransformFeedbackGL(mFunctions, mStateManager, + getRendererCaps().maxTransformFeedbackSeparateComponents); } SamplerImpl *RendererGL::createSampler() diff --git a/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp index 52efd449fd6b..4a6e2f14dd65 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp @@ -36,7 +36,8 @@ ShaderGL::~ShaderGL() } } -int ShaderGL::prepareSourceAndReturnOptions(std::stringstream *sourceStream) +int ShaderGL::prepareSourceAndReturnOptions(std::stringstream *sourceStream, + std::string * /*sourcePath*/) { // Reset the previous state if (mShaderID != 0) @@ -70,7 +71,6 @@ bool ShaderGL::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog // Check for compile errors from the native driver GLint compileStatus = GL_FALSE; mFunctions->getShaderiv(mShaderID, GL_COMPILE_STATUS, &compileStatus); - ASSERT(compileStatus == GL_TRUE); if (compileStatus == GL_FALSE) { // Compilation failed, put the error into the info log diff --git a/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h b/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h index c3e0982e87c2..76139ace8f38 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h @@ -25,7 +25,8 @@ class ShaderGL : public ShaderImpl ~ShaderGL() override; // ShaderImpl implementation - int prepareSourceAndReturnOptions(std::stringstream *sourceStream) override; + int prepareSourceAndReturnOptions(std::stringstream *sourceStream, + std::string *sourcePath) override; bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override; std::string getDebugInfo() const override; diff --git a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp index 151947251f7b..9976e5bb5ae5 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp @@ -11,6 +11,7 @@ #include "common/BitSetIterator.h" #include "libANGLE/Data.h" #include "libANGLE/Framebuffer.h" +#include "libANGLE/TransformFeedback.h" #include "libANGLE/VertexArray.h" #include "libANGLE/Query.h" #include "libANGLE/renderer/gl/BufferGL.h" @@ -19,6 +20,7 @@ #include "libANGLE/renderer/gl/ProgramGL.h" #include "libANGLE/renderer/gl/SamplerGL.h" #include "libANGLE/renderer/gl/TextureGL.h" +#include "libANGLE/renderer/gl/TransformFeedbackGL.h" #include "libANGLE/renderer/gl/VertexArrayGL.h" #include "libANGLE/renderer/gl/QueryGL.h" @@ -42,7 +44,9 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren mTextureUnitIndex(0), mTextures(), mSamplers(rendererCaps.maxCombinedTextureImageUnits, 0), + mTransformFeedback(0), mQueries(), + mPrevDrawTransformFeedback(nullptr), mPrevDrawQueries(), mPrevDrawContext(0), mUnpackAlignment(4), @@ -259,6 +263,25 @@ void StateManagerGL::deleteRenderbuffer(GLuint rbo) } } +void StateManagerGL::deleteTransformFeedback(GLuint transformFeedback) +{ + if (transformFeedback != 0) + { + if (mTransformFeedback == transformFeedback) + { + bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); + } + + if (mPrevDrawTransformFeedback != nullptr && + mPrevDrawTransformFeedback->getTransformFeedbackID() == transformFeedback) + { + mPrevDrawTransformFeedback = nullptr; + } + + mFunctions->deleteTransformFeedbacks(1, &transformFeedback); + } +} + void StateManagerGL::deleteQuery(GLuint query) { if (query != 0) @@ -516,6 +539,26 @@ void StateManagerGL::bindRenderbuffer(GLenum type, GLuint renderbuffer) } } +void StateManagerGL::bindTransformFeedback(GLenum type, GLuint transformFeedback) +{ + ASSERT(type == GL_TRANSFORM_FEEDBACK); + if (mTransformFeedback != transformFeedback) + { + // Pause the current transform feedback if one is active. + // To handle virtualized contexts, StateManagerGL needs to be able to bind a new transform + // feedback at any time, even if there is one active. + if (mPrevDrawTransformFeedback != nullptr && + mPrevDrawTransformFeedback->getTransformFeedbackID() != transformFeedback) + { + mPrevDrawTransformFeedback->syncPausedState(true); + mPrevDrawTransformFeedback = nullptr; + } + + mTransformFeedback = transformFeedback; + mFunctions->bindTransformFeedback(type, mTransformFeedback); + } +} + void StateManagerGL::beginQuery(GLenum type, GLuint query) { // Make sure this is a valid query type and there is no current active query of this type @@ -594,6 +637,25 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) { const gl::State &state = *data.state; + // If the context has changed, pause the previous context's transform feedback and queries + if (data.context != mPrevDrawContext) + { + if (mPrevDrawTransformFeedback != nullptr) + { + mPrevDrawTransformFeedback->syncPausedState(true); + } + + for (QueryGL *prevQuery : mPrevDrawQueries) + { + prevQuery->pause(); + } + } + mPrevDrawTransformFeedback = nullptr; + mPrevDrawQueries.clear(); + + mPrevDrawContext = data.context; + + // Sync the current program state const gl::Program *program = state.getProgram(); const ProgramGL *programGL = GetImplAs(program); useProgram(programGL->getProgramID()); @@ -671,17 +733,23 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) // Seamless cubemaps are required for ES3 and higher contexts. setTextureCubemapSeamlessEnabled(data.clientVersion >= 3); - // If the context has changed, pause the previous context's queries - if (data.context != mPrevDrawContext) + // Set the current transform feedback state + gl::TransformFeedback *transformFeedback = state.getCurrentTransformFeedback(); + if (transformFeedback) { - for (QueryGL *prevQuery : mPrevDrawQueries) - { - prevQuery->pause(); - } + TransformFeedbackGL *transformFeedbackGL = + GetImplAs(transformFeedback); + bindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbackGL->getTransformFeedbackID()); + transformFeedbackGL->syncActiveState(transformFeedback->isActive(), + transformFeedback->getPrimitiveMode()); + transformFeedbackGL->syncPausedState(transformFeedback->isPaused()); + mPrevDrawTransformFeedback = transformFeedbackGL; + } + else + { + bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); + mPrevDrawTransformFeedback = nullptr; } - mPrevDrawQueries.clear(); - - mPrevDrawContext = data.context; // Set the current query state for (GLenum queryType : QueryTypes) diff --git a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h index 6c7120b9b7d9..b430208ae201 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h @@ -28,6 +28,7 @@ namespace rx { class FunctionsGL; +class TransformFeedbackGL; class QueryGL; class StateManagerGL final : angle::NonCopyable @@ -42,6 +43,7 @@ class StateManagerGL final : angle::NonCopyable void deleteBuffer(GLuint buffer); void deleteFramebuffer(GLuint fbo); void deleteRenderbuffer(GLuint rbo); + void deleteTransformFeedback(GLuint transformFeedback); void deleteQuery(GLuint query); void useProgram(GLuint program); @@ -54,6 +56,7 @@ class StateManagerGL final : angle::NonCopyable void bindSampler(size_t unit, GLuint sampler); void bindFramebuffer(GLenum type, GLuint framebuffer); void bindRenderbuffer(GLenum type, GLuint renderbuffer); + void bindTransformFeedback(GLenum type, GLuint transformFeedback); void beginQuery(GLenum type, GLuint query); void endQuery(GLenum type, GLuint query); @@ -162,8 +165,11 @@ class StateManagerGL final : angle::NonCopyable std::map> mTextures; std::vector mSamplers; + GLuint mTransformFeedback; + std::map mQueries; + TransformFeedbackGL *mPrevDrawTransformFeedback; std::set mPrevDrawQueries; uintptr_t mPrevDrawContext; diff --git a/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp index 62754448c19d..c325bb199a90 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp @@ -9,51 +9,134 @@ #include "libANGLE/renderer/gl/TransformFeedbackGL.h" #include "common/debug.h" +#include "libANGLE/Data.h" +#include "libANGLE/renderer/gl/BufferGL.h" +#include "libANGLE/renderer/gl/FunctionsGL.h" +#include "libANGLE/renderer/gl/StateManagerGL.h" namespace rx { -TransformFeedbackGL::TransformFeedbackGL() - : TransformFeedbackImpl() -{} +TransformFeedbackGL::TransformFeedbackGL(const FunctionsGL *functions, + StateManagerGL *stateManager, + size_t maxTransformFeedbackBufferBindings) + : TransformFeedbackImpl(), + mFunctions(functions), + mStateManager(stateManager), + mTransformFeedbackID(0), + mIsActive(false), + mIsPaused(false), + mCurrentIndexedBuffers(maxTransformFeedbackBufferBindings) +{ + mFunctions->genTransformFeedbacks(1, &mTransformFeedbackID); +} TransformFeedbackGL::~TransformFeedbackGL() -{} +{ + mStateManager->deleteTransformFeedback(mTransformFeedbackID); + mTransformFeedbackID = 0; + + for (auto &bufferBinding : mCurrentIndexedBuffers) + { + bufferBinding.set(nullptr); + } +} void TransformFeedbackGL::begin(GLenum primitiveMode) { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); + // Do not begin directly, StateManagerGL will handle beginning and resuming transform feedback. } void TransformFeedbackGL::end() { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); + syncActiveState(false, GL_NONE); } void TransformFeedbackGL::pause() { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); + syncPausedState(true); } void TransformFeedbackGL::resume() { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); + // Do not resume directly, StateManagerGL will handle beginning and resuming transform feedback. } void TransformFeedbackGL::bindGenericBuffer(const BindingPointer &binding) { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); } void TransformFeedbackGL::bindIndexedBuffer(size_t index, const OffsetBindingPointer &binding) { - // Skipped to prevent assertions in tests - // UNIMPLEMENTED(); + // Directly bind buffer (not through the StateManager methods) because the buffer bindings are + // tracked per transform feedback object + if (binding != mCurrentIndexedBuffers[index]) + { + mStateManager->bindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedbackID); + if (binding.get() != nullptr) + { + const BufferGL *bufferGL = GetImplAs(binding.get()); + if (binding.getSize() != 0) + { + mFunctions->bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, + static_cast(index), bufferGL->getBufferID(), + binding.getOffset(), binding.getSize()); + } + else + { + mFunctions->bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast(index), + bufferGL->getBufferID()); + } + } + else + { + mFunctions->bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast(index), 0); + } + + mCurrentIndexedBuffers[index] = binding; + } +} + +GLuint TransformFeedbackGL::getTransformFeedbackID() const +{ + return mTransformFeedbackID; +} + +void TransformFeedbackGL::syncActiveState(bool active, GLenum primitiveMode) const +{ + if (mIsActive != active) + { + mIsActive = active; + mIsPaused = false; + + mStateManager->bindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedbackID); + if (mIsActive) + { + mFunctions->beginTransformFeedback(primitiveMode); + } + else + { + mFunctions->endTransformFeedback(); + } + } +} + +void TransformFeedbackGL::syncPausedState(bool paused) const +{ + if (mIsActive && mIsPaused != paused) + { + mIsPaused = paused; + + mStateManager->bindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedbackID); + if (mIsPaused) + { + mFunctions->pauseTransformFeedback(); + } + else + { + mFunctions->resumeTransformFeedback(); + } + } } } diff --git a/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h b/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h index 9008d4e5aeea..7f72077e83b7 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h +++ b/gfx/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h @@ -14,10 +14,15 @@ namespace rx { +class FunctionsGL; +class StateManagerGL; + class TransformFeedbackGL : public TransformFeedbackImpl { public: - TransformFeedbackGL(); + TransformFeedbackGL(const FunctionsGL *functions, + StateManagerGL *stateManager, + size_t maxTransformFeedbackBufferBindings); ~TransformFeedbackGL() override; void begin(GLenum primitiveMode) override; @@ -27,6 +32,22 @@ class TransformFeedbackGL : public TransformFeedbackImpl void bindGenericBuffer(const BindingPointer &binding) override; void bindIndexedBuffer(size_t index, const OffsetBindingPointer &binding) override; + + GLuint getTransformFeedbackID() const; + + void syncActiveState(bool active, GLenum primitiveMode) const; + void syncPausedState(bool paused) const; + + private: + const FunctionsGL *mFunctions; + StateManagerGL *mStateManager; + + GLuint mTransformFeedbackID; + + mutable bool mIsActive; + mutable bool mIsPaused; + + std::vector> mCurrentIndexedBuffers; }; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp index 6227a511c15d..708566370be7 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp @@ -50,7 +50,7 @@ VertexArrayGL::VertexArrayGL(const VertexArray::Data &data, mFunctions->genVertexArrays(1, &mVertexArrayID); // Set the cached vertex attribute array size - GLint maxVertexAttribs; + GLint maxVertexAttribs = 0; mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); mAppliedAttributes.resize(maxVertexAttribs); } diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm index 7e25a9bcad2d..9037bbdf9acd 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm @@ -151,6 +151,10 @@ egl::ConfigSet DisplayCGL::generateConfigs() const // TODO(cwallez): generate more config permutations egl::ConfigSet configs; + const gl::Version &maxVersion = getMaxSupportedESVersion(); + ASSERT(maxVersion >= gl::Version(2, 0)); + bool supportsES3 = maxVersion >= gl::Version(3, 0); + egl::Config config; // Native stuff @@ -197,8 +201,8 @@ egl::ConfigSet DisplayCGL::generateConfigs() const config.renderTargetFormat = GL_RGBA8; config.depthStencilFormat = GL_DEPTH24_STENCIL8; - config.conformant = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR; - config.renderableType = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR; + config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); + config.renderableType = config.conformant; config.matchNativePixmap = EGL_NONE; diff --git a/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp b/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp index 75e24f0f4aa4..b2f24f2af990 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp @@ -227,15 +227,19 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap() InsertFormatMapping(&map, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, VersionOrExts(4, 3, "GL_ARB_ES3_compatibility"), Always(), Never(), VersionOnly(3, 0), Always(), Never()); // From GL_EXT_texture_compression_dxt1 - // | Format | OpenGL texture support | Filter | Render | OpenGL ES texture support | Filter | Render | - InsertFormatMapping(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_EXT_texture_compression_dxt1"), Always(), Never()); - InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_EXT_texture_compression_dxt1"), Always(), Never()); + // | Format | OpenGL texture support | Filter | Render | OpenGL ES texture support | Filter | Render | + InsertFormatMapping(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_EXT_texture_compression_dxt1"), Always(), Never()); + InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_EXT_texture_compression_dxt1"), Always(), Never()); // From GL_ANGLE_texture_compression_dxt3 - InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_ANGLE_texture_compression_dxt3"), Always(), Never()); + InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_ANGLE_texture_compression_dxt3"), Always(), Never()); // From GL_ANGLE_texture_compression_dxt5 - InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_ANGLE_texture_compression_dxt5"), Always(), Never()); + InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, ExtsOnly("GL_EXT_texture_compression_s3tc"), Always(), Never(), ExtsOnly("GL_ANGLE_texture_compression_dxt5"), Always(), Never()); + + // From GL_ETC1_RGB8_OES + InsertFormatMapping(&map, GL_ETC1_RGB8_OES, VersionOrExts(4, 3, "GL_ARB_ES3_compatibility"), Always(), Never(), VersionOrExts(3, 0, "GL_ETC1_RGB8_OES"), Always(), Never()); + // clang-format on return map; @@ -372,6 +376,35 @@ static GLenum GetNativeFormat(const FunctionsGL *functions, return result; } +static GLenum GetNativeCompressedFormat(const FunctionsGL *functions, + const WorkaroundsGL &workarounds, + GLenum format) +{ + GLenum result = format; + + if (functions->standard == STANDARD_GL_DESKTOP) + { + if (format == GL_ETC1_RGB8_OES) + { + // GL_ETC1_RGB8_OES is not available in any desktop GL extension but the compression + // format is forwards compatible so just use the ETC2 format. + result = GL_COMPRESSED_RGB8_ETC2; + } + } + + if (functions->isAtLeastGLES(gl::Version(3, 0))) + { + if (format == GL_ETC1_RGB8_OES) + { + // Pass GL_COMPRESSED_RGB8_ETC2 as the target format in ES3 and higher because it + // becomes a core format. + result = GL_COMPRESSED_RGB8_ETC2; + } + } + + return result; +} + static GLenum GetNativeType(const FunctionsGL *functions, const WorkaroundsGL &workarounds, GLenum type) @@ -429,7 +462,7 @@ CompressedTexImageFormat GetCompressedTexImageFormat(const FunctionsGL *function GLenum internalFormat) { CompressedTexImageFormat result; - result.internalFormat = internalFormat; + result.internalFormat = GetNativeCompressedFormat(functions, workarounds, internalFormat); return result; } @@ -438,7 +471,7 @@ CompressedTexSubImageFormat GetCompressedSubTexImageFormat(const FunctionsGL *fu GLenum format) { CompressedTexSubImageFormat result; - result.format = format; + result.format = GetNativeCompressedFormat(functions, workarounds, format); return result; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp index 1d2d073ea8e9..7ba9fd660ce0 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp @@ -21,6 +21,18 @@ namespace rx { +static int IgnoreX11Errors(Display *, XErrorEvent *) +{ + return 0; +} + +SwapControlData::SwapControlData() + : targetSwapInterval(0), + maxSwapInterval(-1), + currentSwapInterval(-1) +{ +} + class FunctionsGLGLX : public FunctionsGL { public: @@ -49,6 +61,13 @@ DisplayGLX::DisplayGLX() mContext(nullptr), mDummyPbuffer(0), mUsesNewXDisplay(false), + mIsMesa(false), + mHasMultisample(false), + mHasARBCreateContext(false), + mSwapControl(SwapControl::Absent), + mMinSwapInterval(0), + mMaxSwapInterval(0), + mCurrentSwapInterval(-1), mEGLDisplay(nullptr) { } @@ -81,19 +100,41 @@ egl::Error DisplayGLX::initialize(egl::Display *display) return egl::Error(EGL_NOT_INITIALIZED, glxInitError.c_str()); } - // Check we have the needed extensions + mHasMultisample = mGLX.minorVersion > 3 || mGLX.hasExtension("GLX_ARB_multisample"); + mHasARBCreateContext = mGLX.hasExtension("GLX_ARB_create_context"); + + // Choose the swap_control extension to use, if any. + // The EXT version is better as it allows glXSwapInterval to be called per + // window, while we'll potentially need to change the swap interval on each + // swap buffers when using the SGI or MESA versions. + if (mGLX.hasExtension("GLX_EXT_swap_control")) { - if (mGLX.minorVersion == 3 && !mGLX.hasExtension("GLX_ARB_multisample")) - { - return egl::Error(EGL_NOT_INITIALIZED, "GLX doesn't support ARB_multisample."); - } - // Require ARB_create_context which has been supported since Mesa 9 unconditionnaly - // and is present in Mesa 8 in an almost always on compile flag. Also assume proprietary - // drivers have it. - if (!mGLX.hasExtension("GLX_ARB_create_context")) - { - return egl::Error(EGL_NOT_INITIALIZED, "GLX doesn't support ARB_create_context."); - } + mSwapControl = SwapControl::EXT; + + // In GLX_EXT_swap_control querying these is done on a GLXWindow so we just + // set default values. + mMinSwapInterval = 0; + mMaxSwapInterval = 4; + } + else if (mGLX.hasExtension("GLX_MESA_swap_control")) + { + // If we have the Mesa or SGI extension, assume that you can at least set + // a swap interval of 0 or 1. + mSwapControl = SwapControl::Mesa; + mMinSwapInterval = 0; + mMinSwapInterval = 1; + } + else if (mGLX.hasExtension("GLX_SGI_swap_control")) + { + mSwapControl = SwapControl::SGI; + mMinSwapInterval = 0; + mMinSwapInterval = 1; + } + else + { + mSwapControl = SwapControl::Absent; + mMinSwapInterval = 1; + mMinSwapInterval = 1; } // When glXMakeCurrent is called, the context and the surface must be @@ -137,7 +178,27 @@ egl::Error DisplayGLX::initialize(egl::Display *display) XFree(candidates); } - mContext = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, nullptr); + if (mHasARBCreateContext) + { + mContext = initializeContext(mContextConfig, display->getAttributeMap()); + } + else + { + XVisualInfo visualTemplate; + visualTemplate.visualid = getGLXFBConfigAttrib(mContextConfig, GLX_VISUAL_ID); + + int numVisuals = 0; + XVisualInfo *visuals = XGetVisualInfo(xDisplay, VisualIDMask, &visualTemplate, &numVisuals); + if (numVisuals <= 0) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Could not get the visual info from the fb config"); + } + ASSERT(numVisuals == 1); + + mContext = mGLX.createContext(&visuals[0], nullptr, true); + XFree(visuals); + } if (!mContext) { return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); @@ -146,11 +207,20 @@ egl::Error DisplayGLX::initialize(egl::Display *display) // FunctionsGL and DisplayGL need to make a few GL calls, for example to // query the version of the context so we need to make the context current. // glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer - // (of size 0, 0) for the duration of these calls. + // (of size 1, 1) for the duration of these calls. + // Ideally we would want to unset the current context and destroy the pbuffer + // before going back to the application but this is TODO + // We could use a pbuffer of size (0, 0) but it fails on the Intel Mesa driver + // as commented on https://bugs.freedesktop.org/show_bug.cgi?id=38869 so we + // use (1, 1) instead. - // to query things like limits. Ideally we would want to unset the current context - // and destroy the pbuffer before going back to the application but this is TODO - mDummyPbuffer = mGLX.createPbuffer(mContextConfig, nullptr); + int dummyPbufferAttribs[] = + { + GLX_PBUFFER_WIDTH, 1, + GLX_PBUFFER_HEIGHT, 1, + None, + }; + mDummyPbuffer = mGLX.createPbuffer(mContextConfig, dummyPbufferAttribs); if (!mDummyPbuffer) { return egl::Error(EGL_NOT_INITIALIZED, "Could not create the dummy pbuffer."); @@ -166,6 +236,10 @@ egl::Error DisplayGLX::initialize(egl::Display *display) syncXCommands(); + std::string rendererString = + reinterpret_cast(mFunctionsGL->getString(GL_RENDERER)); + mIsMesa = rendererString.find("Mesa") != std::string::npos; + return DisplayGL::initialize(display); } @@ -237,15 +311,103 @@ egl::Error DisplayGLX::getDevice(DeviceImpl **device) return egl::Error(EGL_BAD_DISPLAY); } +glx::Context DisplayGLX::initializeContext(glx::FBConfig config, + const egl::AttributeMap &eglAttributes) +{ + // Create a context of the requested version, if any. + EGLint requestedMajorVersion = + eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE); + EGLint requestedMinorVersion = + eglAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); + if (requestedMajorVersion != EGL_DONT_CARE && requestedMinorVersion != EGL_DONT_CARE) + { + std::vector contextAttributes; + contextAttributes.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); + contextAttributes.push_back(requestedMajorVersion); + + contextAttributes.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); + contextAttributes.push_back(requestedMinorVersion); + + contextAttributes.push_back(None); + return createContextAttribs(config, contextAttributes); + } + + // It is commonly assumed that glXCreateContextAttrib will create a context + // of the highest version possible but it is not specified in the spec and + // is not true on the Mesa drivers. Instead we try to create a context per + // desktop GL version until we succeed, starting from newer version. + // clang-format off + const gl::Version desktopVersions[] = { + gl::Version(4, 5), + gl::Version(4, 4), + gl::Version(4, 3), + gl::Version(4, 2), + gl::Version(4, 1), + gl::Version(4, 0), + gl::Version(3, 3), + gl::Version(3, 2), + gl::Version(3, 1), + gl::Version(3, 0), + gl::Version(2, 0), + gl::Version(1, 5), + gl::Version(1, 4), + gl::Version(1, 3), + gl::Version(1, 2), + gl::Version(1, 1), + gl::Version(1, 0), + }; + // clang-format on + + bool useProfile = mGLX.hasExtension("GLX_ARB_create_context_profile"); + for (size_t i = 0; i < ArraySize(desktopVersions); ++i) + { + const auto &version = desktopVersions[i]; + + std::vector contextAttributes; + contextAttributes.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); + contextAttributes.push_back(version.major); + + contextAttributes.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); + contextAttributes.push_back(version.minor); + + if (useProfile && version >= gl::Version(3, 2)) + { + contextAttributes.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); + contextAttributes.push_back(GLX_CONTEXT_CORE_PROFILE_BIT_ARB); + } + + contextAttributes.push_back(None); + auto context = createContextAttribs(config, contextAttributes); + + if (context) + { + return context; + } + } + + return nullptr; +} + egl::ConfigSet DisplayGLX::generateConfigs() const { egl::ConfigSet configs; configIdToGLXConfig.clear(); - bool hasSwapControl = mGLX.hasExtension("GLX_EXT_swap_control"); + const gl::Version &maxVersion = getMaxSupportedESVersion(); + ASSERT(maxVersion >= gl::Version(2, 0)); + bool supportsES3 = maxVersion >= gl::Version(3, 0); - int contextSamples = getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLES); - int contextSampleBuffers = getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLE_BUFFERS); + int contextRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_RED_SIZE); + int contextGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_GREEN_SIZE); + int contextBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_BLUE_SIZE); + int contextAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ALPHA_SIZE); + + int contextDepthSize = getGLXFBConfigAttrib(mContextConfig, GLX_DEPTH_SIZE); + int contextStencilSize = getGLXFBConfigAttrib(mContextConfig, GLX_STENCIL_SIZE); + + int contextSamples = mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLES) : 0; + int contextSampleBuffers = + mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLE_BUFFERS) : 0; int contextAccumRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_RED_SIZE); int contextAccumGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_GREEN_SIZE); @@ -282,11 +444,19 @@ egl::ConfigSet DisplayGLX::generateConfigs() const config.stencilSize = getGLXFBConfigAttrib(glxConfig, GLX_STENCIL_SIZE); // We require RGBA8 and the D24S8 (or no DS buffer) - if (config.redSize != 8 || config.greenSize != 8 || config.blueSize != 8 || config.alphaSize != 8) + if (config.redSize != contextRedSize || config.greenSize != contextGreenSize || + config.blueSize != contextBlueSize || config.alphaSize != contextAlphaSize) { continue; } - if (!(config.depthSize == 24 && config.stencilSize == 8) && !(config.depthSize == 0 && config.stencilSize == 0)) + // The GLX spec says that it is ok for a whole buffer to not be present + // however the Mesa Intel driver (and probably on other Mesa drivers) + // fails to make current when the Depth stencil doesn't exactly match the + // configuration. + bool hasSameDepthStencil = + config.depthSize == contextDepthSize && config.stencilSize == contextStencilSize; + bool hasNoDepthStencil = config.depthSize == 0 && config.stencilSize == 0; + if (!hasSameDepthStencil && (mIsMesa || !hasNoDepthStencil)) { continue; } @@ -298,8 +468,9 @@ egl::ConfigSet DisplayGLX::generateConfigs() const config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; // Multisample and accumulation buffers - int samples = getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES); - int sampleBuffers = getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS); + int samples = mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES) : 0; + int sampleBuffers = + mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS) : 0; int accumRedSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_RED_SIZE); int accumGreenSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_GREEN_SIZE); @@ -362,23 +533,16 @@ egl::ConfigSet DisplayGLX::generateConfigs() const (glxDrawable & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0) | (glxDrawable & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0); - if (hasSwapControl) - { - // In GLX_EXT_swap_control querying these is done on a GLXWindow so we just set a default value. - config.minSwapInterval = 0; - config.maxSwapInterval = 4; - } - else - { - config.minSwapInterval = 1; - config.maxSwapInterval = 1; - } + config.minSwapInterval = mMinSwapInterval; + config.maxSwapInterval = mMaxSwapInterval; + // TODO(cwallez) wildly guessing these formats, another TODO says they should be removed anyway config.renderTargetFormat = GL_RGBA8; config.depthStencilFormat = GL_DEPTH24_STENCIL8; - // TODO(cwallez) Fill after determining the GL version we are using and what ES version it supports - config.conformant = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR; - config.renderableType = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR; + + config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); + config.renderableType = config.conformant; + // TODO(cwallez) I have no idea what this is config.matchNativePixmap = EGL_NONE; @@ -445,6 +609,49 @@ void DisplayGLX::syncXCommands() const } } +void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data) +{ + ASSERT(data != nullptr); + + // TODO(cwallez) error checking? + if (mSwapControl == SwapControl::EXT) + { + // Prefer the EXT extension, it gives per-drawable swap intervals, which will + // minimize the number of driver calls. + if (data->maxSwapInterval < 0) + { + unsigned int maxSwapInterval = 0; + mGLX.queryDrawable(drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxSwapInterval); + data->maxSwapInterval = static_cast(maxSwapInterval); + } + + // When the egl configs were generated we had to guess what the max swap interval + // was because we didn't have a window to query it one (and that this max could + // depend on the monitor). This means that the target interval might be higher + // than the max interval and needs to be clamped. + const int realInterval = std::min(data->targetSwapInterval, data->maxSwapInterval); + if (data->currentSwapInterval != realInterval) + { + mGLX.swapIntervalEXT(drawable, realInterval); + data->currentSwapInterval = realInterval; + } + } + else if (mCurrentSwapInterval != data->targetSwapInterval) + { + // With the Mesa or SGI extensions we can still do per-drawable swap control + // manually but it is more expensive in number of driver calls. + if (mSwapControl == SwapControl::Mesa) + { + mGLX.swapIntervalMESA(data->targetSwapInterval); + } + else if (mSwapControl == SwapControl::Mesa) + { + mGLX.swapIntervalSGI(data->targetSwapInterval); + } + mCurrentSwapInterval = data->targetSwapInterval; + } +} + const FunctionsGL *DisplayGLX::getFunctionsGL() const { return mFunctionsGL; @@ -468,4 +675,15 @@ int DisplayGLX::getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const return result; } +glx::Context DisplayGLX::createContextAttribs(glx::FBConfig, const std::vector &attribs) const +{ + // When creating a context with glXCreateContextAttribsARB, a variety of X11 errors can + // be generated. To prevent these errors from crashing our process, we simply ignore + // them and only look if GLXContext was created. + auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors); + auto context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data()); + XSetErrorHandler(oldErrorHandler); + + return context; +} } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h index e72d9a90b3a8..5fd6abb7671c 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h @@ -20,6 +20,20 @@ namespace rx class FunctionsGLX; +// State-tracking data for the swap control to allow DisplayGLX to remember per +// drawable information for swap control. +struct SwapControlData +{ + SwapControlData(); + + // Set by the drawable + int targetSwapInterval; + + // DisplayGLX-side state-tracking + int maxSwapInterval; + int currentSwapInterval; +}; + class DisplayGLX : public DisplayGL { public: @@ -59,13 +73,22 @@ class DisplayGLX : public DisplayGL // between the application's display and ANGLE's one. void syncXCommands() const; + // Depending on the supported GLX extension, swap interval can be set + // globally or per drawable. This function will make sure the drawable's + // swap interval is the one required so that the subsequent swapBuffers + // acts as expected. + void setSwapInterval(glx::Drawable drawable, SwapControlData *data); + private: const FunctionsGL *getFunctionsGL() const override; + glx::Context initializeContext(glx::FBConfig config, const egl::AttributeMap &eglAttributes); + void generateExtensions(egl::DisplayExtensions *outExtensions) const override; void generateCaps(egl::Caps *outCaps) const override; int getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const; + glx::Context createContextAttribs(glx::FBConfig, const std::vector &attribs) const; FunctionsGL *mFunctionsGL; @@ -78,6 +101,21 @@ class DisplayGLX : public DisplayGL glx::Pbuffer mDummyPbuffer; bool mUsesNewXDisplay; + bool mIsMesa; + bool mHasMultisample; + bool mHasARBCreateContext; + + enum class SwapControl + { + Absent, + EXT, + Mesa, + SGI, + }; + SwapControl mSwapControl; + int mMinSwapInterval; + int mMaxSwapInterval; + int mCurrentSwapInterval; FunctionsGLX mGLX; egl::Display *mEGLDisplay; diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/FBConfigCompatibility.md b/gfx/angle/src/libANGLE/renderer/gl/glx/FBConfigCompatibility.md index e5af46aff77e..204a4881c49e 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/FBConfigCompatibility.md +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/FBConfigCompatibility.md @@ -155,6 +155,37 @@ have the same multisample and accumulation buffers gives the following: 0x03f 24 dc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None ``` +Mesa Intel driver +----------------- +In GLX, a criterium for context and surface compatibility is that buffers +should have the same depth, if they exist at all in the surface. This means +that it should be possible to make a context with a D24S8 depth-stencil +buffer to a surface without a depth-stencil buffer. This doesn't work on the +Mesa Intel driver. The list before the workaround was the following, with +0x020 being the fbconfig chosen for the context: +``` + visual x bf lv rg d st colorbuffer sr ax dp st accumbuffer ms cav + id dep cl sp sz l ci b ro r g b a F gb bf th cl r g b a ns b eat +---------------------------------------------------------------------------- +0x020 24 tc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x021 24 dc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x08f 32 tc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x0d0 24 tc 0 32 0 r y . 8 8 8 8 . . 0 0 0 0 0 0 0 0 0 None +0x0e2 24 dc 0 32 0 r y . 8 8 8 8 . . 0 0 0 0 0 0 0 0 0 None +0x0e9 24 dc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +``` + +After the workaround that list becomes the following: +``` + visual x bf lv rg d st colorbuffer sr ax dp st accumbuffer ms cav + id dep cl sp sz l ci b ro r g b a F gb bf th cl r g b a ns b eat +---------------------------------------------------------------------------- +0x020 24 tc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x021 24 dc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x08f 32 tc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +0x0e9 24 dc 0 32 0 r y . 8 8 8 8 . . 0 24 8 0 0 0 0 0 0 None +``` + Future investigation -------------------- All the non-conformant configs have a multisampled buffer, so it could be interesting @@ -167,3 +198,4 @@ drivers and hardware. The drivers tested were: - the proprietary NVIDIA driver - the proprietary AMD driver + - the open source Intel (Broadwell) Mesa driver diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp index 4dfc4351d8c6..a93cea2287c8 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp @@ -35,34 +35,42 @@ static bool GetProc(PFNGETPROCPROC getProc, T *member, const char *name) struct FunctionsGLX::GLXFunctionTable { GLXFunctionTable() - : destroyContextPtr(nullptr), - makeCurrentPtr(nullptr), - swapBuffersPtr(nullptr), - queryExtensionPtr(nullptr), - queryVersionPtr(nullptr), - waitXPtr(nullptr), - waitGLPtr(nullptr), - queryExtensionsStringPtr(nullptr), - getFBConfigsPtr(nullptr), - chooseFBConfigPtr(nullptr), - getFBConfigAttribPtr(nullptr), - getVisualFromFBConfigPtr(nullptr), - createWindowPtr(nullptr), - destroyWindowPtr(nullptr), - createPbufferPtr(nullptr), - destroyPbufferPtr(nullptr), - queryDrawablePtr(nullptr), - createContextAttribsARBPtr(nullptr), - swapIntervalEXTPtr(nullptr) + : createContextPtr(nullptr), + destroyContextPtr(nullptr), + makeCurrentPtr(nullptr), + swapBuffersPtr(nullptr), + queryExtensionPtr(nullptr), + queryVersionPtr(nullptr), + getCurrentContextPtr(nullptr), + getCurrentDrawablePtr(nullptr), + waitXPtr(nullptr), + waitGLPtr(nullptr), + queryExtensionsStringPtr(nullptr), + getFBConfigsPtr(nullptr), + chooseFBConfigPtr(nullptr), + getFBConfigAttribPtr(nullptr), + getVisualFromFBConfigPtr(nullptr), + createWindowPtr(nullptr), + destroyWindowPtr(nullptr), + createPbufferPtr(nullptr), + destroyPbufferPtr(nullptr), + queryDrawablePtr(nullptr), + createContextAttribsARBPtr(nullptr), + swapIntervalEXTPtr(nullptr), + swapIntervalMESAPtr(nullptr), + swapIntervalSGIPtr(nullptr) { } // GLX 1.0 + PFNGLXCREATECONTEXTPROC createContextPtr; PFNGLXDESTROYCONTEXTPROC destroyContextPtr; PFNGLXMAKECURRENTPROC makeCurrentPtr; PFNGLXSWAPBUFFERSPROC swapBuffersPtr; PFNGLXQUERYEXTENSIONPROC queryExtensionPtr; PFNGLXQUERYVERSIONPROC queryVersionPtr; + PFNGLXGETCURRENTCONTEXTPROC getCurrentContextPtr; + PFNGLXGETCURRENTDRAWABLEPROC getCurrentDrawablePtr; PFNGLXWAITXPROC waitXPtr; PFNGLXWAITGLPROC waitGLPtr; @@ -85,6 +93,12 @@ struct FunctionsGLX::GLXFunctionTable // GLX_EXT_swap_control PFNGLXSWAPINTERVALEXTPROC swapIntervalEXTPtr; + + // GLX_MESA_swap_control + PFNGLXSWAPINTERVALMESAPROC swapIntervalMESAPtr; + + // GLX_SGI_swap_control + PFNGLXSWAPINTERVALSGIPROC swapIntervalSGIPtr; }; FunctionsGLX::FunctionsGLX() @@ -149,11 +163,14 @@ bool FunctionsGLX::initialize(Display *xDisplay, int screen, std::string *errorS #endif // GLX 1.0 + GET_FNPTR_OR_ERROR(&mFnPtrs->createContextPtr, glXCreateContext); GET_FNPTR_OR_ERROR(&mFnPtrs->destroyContextPtr, glXDestroyContext); GET_FNPTR_OR_ERROR(&mFnPtrs->makeCurrentPtr, glXMakeCurrent); GET_FNPTR_OR_ERROR(&mFnPtrs->swapBuffersPtr, glXSwapBuffers); GET_FNPTR_OR_ERROR(&mFnPtrs->queryExtensionPtr, glXQueryExtension); GET_FNPTR_OR_ERROR(&mFnPtrs->queryVersionPtr, glXQueryVersion); + GET_FNPTR_OR_ERROR(&mFnPtrs->getCurrentContextPtr, glXGetCurrentContext); + GET_FNPTR_OR_ERROR(&mFnPtrs->getCurrentDrawablePtr, glXGetCurrentDrawable); GET_FNPTR_OR_ERROR(&mFnPtrs->waitXPtr, glXWaitX); GET_FNPTR_OR_ERROR(&mFnPtrs->waitGLPtr, glXWaitGL); @@ -207,17 +224,17 @@ bool FunctionsGLX::initialize(Display *xDisplay, int screen, std::string *errorS { GET_PROC_OR_ERROR(&mFnPtrs->createContextAttribsARBPtr, glXCreateContextAttribsARB); } - else - { - mFnPtrs->createContextAttribsARBPtr = nullptr; - } if (hasExtension("GLX_EXT_swap_control")) { GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalEXTPtr, glXSwapIntervalEXT); } - else + if (hasExtension("GLX_MESA_swap_control")) { - mFnPtrs->swapIntervalEXTPtr = nullptr; + GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalMESAPtr, glXSwapIntervalMESA); + } + if (hasExtension("GLX_SGI_swap_control")) + { + GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalSGIPtr, glXSwapIntervalSGI); } #undef GET_FNPTR_OR_ERROR @@ -249,6 +266,12 @@ int FunctionsGLX::getScreen() const // GLX functions // GLX 1.0 +glx::Context FunctionsGLX::createContext(XVisualInfo *visual, glx::Context share, bool direct) const +{ + GLXContext shareCtx = reinterpret_cast(share); + GLXContext context = mFnPtrs->createContextPtr(mXDisplay, visual, shareCtx, direct); + return reinterpret_cast(context); +} void FunctionsGLX::destroyContext(glx::Context context) const { GLXContext ctx = reinterpret_cast(context); @@ -271,6 +294,16 @@ Bool FunctionsGLX::queryVersion(int *major, int *minor) const { return mFnPtrs->queryVersionPtr(mXDisplay, major, minor); } +glx::Context FunctionsGLX::getCurrentContext() const +{ + GLXContext context = mFnPtrs->getCurrentContextPtr(); + return reinterpret_cast(context); +} +glx::Drawable FunctionsGLX::getCurrentDrawable() const +{ + GLXDrawable drawable = mFnPtrs->getCurrentDrawablePtr(); + return reinterpret_cast(drawable); +} void FunctionsGLX::waitX() const { mFnPtrs->waitXPtr(); @@ -344,4 +377,14 @@ void FunctionsGLX::swapIntervalEXT(glx::Drawable drawable, int intervals) const mFnPtrs->swapIntervalEXTPtr(mXDisplay, drawable, intervals); } +int FunctionsGLX::swapIntervalMESA(int intervals) const +{ + return mFnPtrs->swapIntervalMESAPtr(intervals); +} + +int FunctionsGLX::swapIntervalSGI(int intervals) const +{ + return mFnPtrs->swapIntervalSGIPtr(intervals); +} + } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h index 0afbbbbbd943..a919f4c88b04 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h @@ -37,11 +37,14 @@ class FunctionsGLX PFNGETPROCPROC getProc; // GLX 1.0 + glx::Context createContext(XVisualInfo *visual, glx::Context share, bool direct) const; void destroyContext(glx::Context context) const; Bool makeCurrent(glx::Drawable drawable, glx::Context context) const; void swapBuffers(glx::Drawable drawable) const; Bool queryExtension(int *errorBase, int *event) const; Bool queryVersion(int *major, int *minor) const; + glx::Context getCurrentContext() const; + glx::Drawable getCurrentDrawable() const; void waitX() const; void waitGL() const; @@ -65,6 +68,12 @@ class FunctionsGLX // GLX_EXT_swap_control void swapIntervalEXT(glx::Drawable drawable, int interval) const; + // GLX_MESA_swap_control + int swapIntervalMESA(int interval) const; + + // GLX_SGI_swap_control + int swapIntervalSGI(int interval) const; + private: // So as to isolate GLX from angle we do not include angleutils.h and cannot // use angle::NonCopyable so we replicated it here instead. diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp index d876c6d7a40d..6e2e7f7191d5 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp @@ -43,10 +43,16 @@ PbufferSurfaceGLX::~PbufferSurfaceGLX() egl::Error PbufferSurfaceGLX::initialize() { + // Avoid creating 0-sized PBuffers as it fails on the Intel Mesa driver + // as commented on https://bugs.freedesktop.org/show_bug.cgi?id=38869 so we + // use (w, 1) or (1, h) instead. + int width = std::max(1, static_cast(mWidth)); + int height = std::max(1, static_cast(mHeight)); + const int attribs[] = { - GLX_PBUFFER_WIDTH, static_cast(mWidth), - GLX_PBUFFER_HEIGHT, static_cast(mHeight), + GLX_PBUFFER_WIDTH, width, + GLX_PBUFFER_HEIGHT, height, GLX_LARGEST_PBUFFER, mLargest, None }; diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp index 4224eebecc87..c41982901bb7 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp @@ -28,11 +28,10 @@ WindowSurfaceGLX::WindowSurfaceGLX(const FunctionsGLX &glx, mWindow(0), mDisplay(display), mGLX(glx), - mGLXDisplay(*glxDisplay), + mGLXDisplay(glxDisplay), mContext(context), mFBConfig(fbConfig), - mGLXWindow(0), - mMaxSwapInterval(1) + mGLXWindow(0) { } @@ -48,7 +47,7 @@ WindowSurfaceGLX::~WindowSurfaceGLX() XDestroyWindow(mDisplay, mWindow); } - mGLXDisplay.syncXCommands(); + mGLXDisplay->syncXCommands(); } egl::Error WindowSurfaceGLX::initialize() @@ -95,18 +94,13 @@ egl::Error WindowSurfaceGLX::initialize() 0, visualInfo->depth, InputOutput, visual, attributeMask, &attributes); mGLXWindow = mGLX.createWindow(mFBConfig, mWindow, nullptr); - if (mGLX.hasExtension("GLX_EXT_swap_control")) - { - mGLX.queryDrawable(mGLXWindow, GLX_MAX_SWAP_INTERVAL_EXT, &mMaxSwapInterval); - } - XMapWindow(mDisplay, mWindow); XFlush(mDisplay); XFree(visualInfo); XFreeColormap(mDisplay, colormap); - mGLXDisplay.syncXCommands(); + mGLXDisplay->syncXCommands(); return egl::Error(EGL_SUCCESS); } @@ -140,6 +134,7 @@ egl::Error WindowSurfaceGLX::swap() mGLX.waitX(); } + mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl); mGLX.swapBuffers(mGLXWindow); return egl::Error(EGL_SUCCESS); @@ -171,12 +166,7 @@ egl::Error WindowSurfaceGLX::releaseTexImage(EGLint buffer) void WindowSurfaceGLX::setSwapInterval(EGLint interval) { - if (mGLX.hasExtension("GLX_EXT_swap_control")) - { - // TODO(cwallez) error checking? - const int realInterval = std::min(interval, static_cast(mMaxSwapInterval)); - mGLX.swapIntervalEXT(mGLXWindow, realInterval); - } + mSwapControl.targetSwapInterval = interval; } EGLint WindowSurfaceGLX::getWidth() const diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h index f8447abd683a..fff730fe74ce 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h @@ -10,6 +10,7 @@ #define LIBANGLE_RENDERER_GL_GLX_WINDOWSURFACEGLX_H_ #include "libANGLE/renderer/gl/SurfaceGL.h" +#include "libANGLE/renderer/gl/glx/DisplayGLX.h" #include "libANGLE/renderer/gl/glx/platform_glx.h" namespace rx @@ -55,12 +56,13 @@ class WindowSurfaceGLX : public SurfaceGL Display *mDisplay; const FunctionsGLX &mGLX; - const DisplayGLX &mGLXDisplay; + DisplayGLX *mGLXDisplay; glx::Context mContext; glx::FBConfig mFBConfig; glx::Window mGLXWindow; - unsigned int mMaxSwapInterval; + + SwapControlData mSwapControl; }; } diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h b/gfx/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h index d54f07124ec3..b0ba033c78cd 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h @@ -32,6 +32,8 @@ typedef Bool (*PFNGLXISDIRECTPROC) (Display *dpy, GLXContext ctx); typedef int (*PFNGLXGETCONFIGPROC) (Display *dpy, XVisualInfo *visual, int attrib, int *value); typedef GLXContext (*PFNGLXGETCURRENTCONTEXTPROC) (); typedef GLXDrawable (*PFNGLXGETCURRENTDRAWABLEPROC) (); +typedef GLXContext (*PFNGLXGETCURRENTCONTEXTPROC) (); +typedef GLXDrawable (*PFNGLXGETCURRENTDRAWABLEPROC) (); typedef void (*PFNGLXWAITGLPROC) (); typedef void (*PFNGLXWAITXPROC) (); typedef void (*PFNGLXUSEXFONT) (Font font, int first, int count, int list); diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h b/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h index facacf1a5237..a8d9cdec004b 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/platform_glx.h @@ -111,6 +111,11 @@ #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 #define GLX_CONTEXT_FLAGS_ARB 0x2094 +// GLX_ARB_create_context_profile +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 + // GLX_EXT_texture_from_pixmap #define GLX_TEXTURE_1D_BIT_EXT 0x00000001 #define GLX_TEXTURE_2D_BIT_EXT 0x00000002 diff --git a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp index c705232d9617..fbb4fbf1dd74 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -111,35 +111,35 @@ static gl::TextureCaps GenerateTextureFormatCaps(const FunctionsGL *functions, G static GLint QuerySingleGLInt(const FunctionsGL *functions, GLenum name) { - GLint result; + GLint result = 0; functions->getIntegerv(name, &result); return result; } static GLint QueryGLIntRange(const FunctionsGL *functions, GLenum name, size_t index) { - GLint result[2]; + GLint result[2] = {}; functions->getIntegerv(name, result); return result[index]; } static GLint64 QuerySingleGLInt64(const FunctionsGL *functions, GLenum name) { - GLint64 result; + GLint64 result = 0; functions->getInteger64v(name, &result); return result; } static GLfloat QuerySingleGLFloat(const FunctionsGL *functions, GLenum name) { - GLfloat result; + GLfloat result = 0.0f; functions->getFloatv(name, &result); return result; } static GLfloat QueryGLFloatRange(const FunctionsGL *functions, GLenum name, size_t index) { - GLfloat result[2]; + GLfloat result[2] = {}; functions->getFloatv(name, result); return result[index]; } @@ -478,7 +478,8 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM caps->maxCombinedTextureImageUnits = caps->maxVertexTextureImageUnits + caps->maxTextureImageUnits; // Table 6.34, implementation dependent transform feedback limits - if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->hasGLExtension("GL_EXT_transform_feedback") || + if (functions->isAtLeastGL(gl::Version(4, 0)) || + functions->hasGLExtension("GL_ARB_transform_feedback2") || functions->isAtLeastGLES(gl::Version(3, 0))) { caps->maxTransformFeedbackInterleavedComponents = QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); @@ -527,6 +528,26 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM } } + // Can't support ES3 without the GLSL packing builtins. We have a workaround for all + // desktop OpenGL versions starting from 3.3 with the bit packing extension. + if (!functions->isAtLeastGL(gl::Version(4, 2)) && + !(functions->isAtLeastGL(gl::Version(3, 2)) && + functions->hasGLExtension("GL_ARB_shader_bit_encoding")) && + !functions->hasGLExtension("GL_ARB_shading_language_packing") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + + // ES3 needs to support explicit layout location qualifiers, while it might be possible to + // fake them in our side, we currently don't support that. + if (!functions->isAtLeastGL(gl::Version(3, 3)) && + !functions->hasGLExtension("GL_ARB_explicit_attrib_location") && + !functions->isAtLeastGLES(gl::Version(3, 0))) + { + LimitVersion(maxSupportedESVersion, gl::Version(2, 0)); + } + // Extension support extensions->setTextureExtensionSupport(*textureCapsMap); extensions->elementIndexUint = functions->standard == STANDARD_GL_DESKTOP || @@ -577,6 +598,9 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM extensions->debugMarker = functions->isAtLeastGL(gl::Version(4, 3)) || functions->hasGLExtension("GL_KHR_debug") || functions->isAtLeastGLES(gl::Version(3, 2)) || functions->hasGLESExtension("GL_KHR_debug"); + + // ANGLE emulates vertex array objects in its GL layer + extensions->vertexArrayObject = true; } void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds) diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp index 7f293b5c8754..284b603891f4 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp @@ -233,7 +233,7 @@ egl::Error DisplayWGL::initialize(egl::Display *display) // Request core profile mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - std::vector contextCreationAttibutes; + std::vector contextCreationAttributes; // Don't request a specific version unless the user wants one. WGL will return the highest version // that the driver supports if no version is requested. @@ -242,32 +242,32 @@ egl::Error DisplayWGL::initialize(egl::Display *display) EGLint requestedMinorVersion = displayAttributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE); if (requestedMajorVersion != EGL_DONT_CARE && requestedMinorVersion != EGL_DONT_CARE) { - contextCreationAttibutes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); - contextCreationAttibutes.push_back(requestedMajorVersion); + contextCreationAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); + contextCreationAttributes.push_back(requestedMajorVersion); - contextCreationAttibutes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); - contextCreationAttibutes.push_back(requestedMinorVersion); + contextCreationAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); + contextCreationAttributes.push_back(requestedMinorVersion); } // Set the flag attributes if (flags != 0) { - contextCreationAttibutes.push_back(WGL_CONTEXT_FLAGS_ARB); - contextCreationAttibutes.push_back(flags); + contextCreationAttributes.push_back(WGL_CONTEXT_FLAGS_ARB); + contextCreationAttributes.push_back(flags); } // Set the mask attribute if (mask != 0) { - contextCreationAttibutes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); - contextCreationAttibutes.push_back(mask); + contextCreationAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); + contextCreationAttributes.push_back(mask); } // Signal the end of the attributes - contextCreationAttibutes.push_back(0); - contextCreationAttibutes.push_back(0); + contextCreationAttributes.push_back(0); + contextCreationAttributes.push_back(0); - mWGLContext = mFunctionsWGL->createContextAttribsARB(mDeviceContext, NULL, &contextCreationAttibutes[0]); + mWGLContext = mFunctionsWGL->createContextAttribsARB(mDeviceContext, NULL, &contextCreationAttributes[0]); } // If wglCreateContextAttribsARB is unavailable or failed, try the standard wglCreateContext diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp index 3c7d6c66d476..70e9bd808454 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp @@ -8,6 +8,8 @@ #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" +#include + #include "common/string_utils.h" namespace rx diff --git a/gfx/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp b/gfx/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp index 1fae5a04ed04..641c3fbc4292 100644 --- a/gfx/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp +++ b/gfx/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp @@ -8,7 +8,7 @@ #include "libANGLE/renderer/gl/wgl/wgl_utils.h" -#include "libANGLE/renderer/gl/wgl/functionsWGL.h" +#include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" namespace rx { diff --git a/gfx/angle/src/libANGLE/validationES.cpp b/gfx/angle/src/libANGLE/validationES.cpp index 7ee218348b8c..547f548ca9e8 100644 --- a/gfx/angle/src/libANGLE/validationES.cpp +++ b/gfx/angle/src/libANGLE/validationES.cpp @@ -27,9 +27,11 @@ namespace gl { +const char *g_ExceedsMaxElementErrorMessage = "Element value exceeds maximum element index."; + namespace { -bool ValidateDrawAttribs(gl::Context *context, GLint primcount, GLint maxVertex) +bool ValidateDrawAttribs(ValidationContext *context, GLint primcount, GLint maxVertex) { const gl::State &state = context->getState(); const gl::Program *program = state.getProgram(); @@ -1466,7 +1468,10 @@ bool ValidateCopyTexImageParametersBase(gl::Context *context, GLenum target, GLi return true; } -static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsizei primcount) +static bool ValidateDrawBase(ValidationContext *context, + GLenum mode, + GLsizei count, + GLsizei primcount) { switch (mode) { @@ -1500,10 +1505,16 @@ static bool ValidateDrawBase(Context *context, GLenum mode, GLsizei count, GLsiz if (context->getLimitations().noSeparateStencilRefsAndMasks) { - const gl::DepthStencilState &depthStencilState = state.getDepthStencilState(); - if (depthStencilState.stencilWritemask != depthStencilState.stencilBackWritemask || + const Framebuffer *framebuffer = context->getState().getDrawFramebuffer(); + const FramebufferAttachment *stencilBuffer = framebuffer->getStencilbuffer(); + GLuint stencilBits = stencilBuffer ? stencilBuffer->getStencilSize() : 0; + GLuint minimumRequiredStencilMask = (1 << stencilBits) - 1; + const DepthStencilState &depthStencilState = state.getDepthStencilState(); + if ((depthStencilState.stencilWritemask & minimumRequiredStencilMask) != + (depthStencilState.stencilBackWritemask & minimumRequiredStencilMask) || state.getStencilRef() != state.getStencilBackRef() || - depthStencilState.stencilMask != depthStencilState.stencilBackMask) + (depthStencilState.stencilMask & minimumRequiredStencilMask) != + (depthStencilState.stencilBackMask & minimumRequiredStencilMask)) { // Note: these separate values are not supported in WebGL, due to D3D's limitations. See // Section 6.10 of the WebGL 1.0 spec @@ -1651,7 +1662,7 @@ bool ValidateDrawArraysInstancedANGLE(Context *context, GLenum mode, GLint first return ValidateDrawArraysInstanced(context, mode, first, count, primcount); } -bool ValidateDrawElements(Context *context, +bool ValidateDrawElements(ValidationContext *context, GLenum mode, GLsizei count, GLenum type, @@ -1665,7 +1676,7 @@ bool ValidateDrawElements(Context *context, case GL_UNSIGNED_SHORT: break; case GL_UNSIGNED_INT: - if (!context->getExtensions().elementIndexUint) + if (context->getClientVersion() < 3 && !context->getExtensions().elementIndexUint) { context->recordError(Error(GL_INVALID_ENUM)); return false; @@ -1756,6 +1767,15 @@ bool ValidateDrawElements(Context *context, *indexRangeOut = ComputeIndexRange(type, indices, count, state.isPrimitiveRestartEnabled()); } + // If we use an index greater than our maximum supported index range, return an error. + // The ES3 spec does not specify behaviour here, it is undefined, but ANGLE should always + // return an error if possible here. + if (static_cast(indexRangeOut->end) >= context->getCaps().maxElementIndex) + { + context->recordError(Error(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage)); + return false; + } + if (!ValidateDrawAttribs(context, primcount, static_cast(indexRangeOut->end))) { return false; @@ -2167,4 +2187,41 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context, return true; } + +bool ValidateBindVertexArrayBase(Context *context, GLuint array) +{ + VertexArray *vao = context->getVertexArray(array); + + if (!vao) + { + // The default VAO should always exist + ASSERT(array != 0); + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return true; +} + +bool ValidateDeleteVertexArraysBase(Context *context, GLsizei n) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + return true; +} + +bool ValidateGenVertexArraysBase(Context *context, GLsizei n) +{ + if (n < 0) + { + context->recordError(Error(GL_INVALID_VALUE)); + return false; + } + + return true; +} } diff --git a/gfx/angle/src/libANGLE/validationES.h b/gfx/angle/src/libANGLE/validationES.h index afd1c0cd415f..041c725134ed 100644 --- a/gfx/angle/src/libANGLE/validationES.h +++ b/gfx/angle/src/libANGLE/validationES.h @@ -26,6 +26,7 @@ namespace gl class Context; class Program; class Shader; +class ValidationContext; bool ValidCap(const Context *context, GLenum cap); bool ValidTextureTarget(const Context *context, GLenum target); @@ -93,7 +94,7 @@ bool ValidateDrawArrays(Context *context, GLenum mode, GLint first, GLsizei coun bool ValidateDrawArraysInstanced(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount); bool ValidateDrawArraysInstancedANGLE(Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount); -bool ValidateDrawElements(Context *context, +bool ValidateDrawElements(ValidationContext *context, GLenum mode, GLsizei count, GLenum type, @@ -141,6 +142,13 @@ bool ValidateEGLImageTargetRenderbufferStorageOES(Context *context, egl::Display *display, GLenum target, egl::Image *image); + +bool ValidateBindVertexArrayBase(Context *context, GLuint array); +bool ValidateDeleteVertexArraysBase(Context *context, GLsizei n); +bool ValidateGenVertexArraysBase(Context *context, GLsizei n); + +// Error messages shared here for use in testing. +extern const char *g_ExceedsMaxElementErrorMessage; } #endif // LIBANGLE_VALIDATION_ES_H_ diff --git a/gfx/angle/src/libANGLE/validationES2.cpp b/gfx/angle/src/libANGLE/validationES2.cpp index bb5b7caddda1..fa1b1fd3a577 100644 --- a/gfx/angle/src/libANGLE/validationES2.cpp +++ b/gfx/angle/src/libANGLE/validationES2.cpp @@ -152,6 +152,13 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, return false; } break; + case GL_ETC1_RGB8_OES: + if (!context->getExtensions().compressedETC1RGB8Texture) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + break; default: context->recordError(Error( GL_INVALID_ENUM, "internalformat is not a supported compressed internal format")); @@ -343,6 +350,18 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, return false; } break; + case GL_ETC1_RGB8_OES: + if (context->getExtensions().compressedETC1RGB8Texture) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + else + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + break; case GL_DEPTH_COMPONENT: case GL_DEPTH_STENCIL_OES: if (!context->getExtensions().depthTextures) @@ -501,6 +520,7 @@ bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint le case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE: case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE: + case GL_ETC1_RGB8_OES: context->recordError(Error(GL_INVALID_OPERATION)); return false; case GL_DEPTH_COMPONENT: @@ -641,6 +661,18 @@ bool ValidateES2CopyTexImageParameters(Context* context, GLenum target, GLint le return false; } break; + case GL_ETC1_RGB8_OES: + if (context->getExtensions().compressedETC1RGB8Texture) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + else + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + break; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT32_OES: @@ -758,6 +790,13 @@ bool ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei le return false; } break; + case GL_ETC1_RGB8_OES: + if (!context->getExtensions().compressedETC1RGB8Texture) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + break; case GL_RGBA32F_EXT: case GL_RGB32F_EXT: case GL_ALPHA32F_EXT: @@ -960,4 +999,48 @@ bool ValidateDrawBuffers(Context *context, GLsizei n, const GLenum *bufs) return true; } + +bool ValidateBindVertexArrayOES(Context *context, GLuint array) +{ + if (!context->getExtensions().vertexArrayObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return ValidateBindVertexArrayBase(context, array); +} + +bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n) +{ + if (!context->getExtensions().vertexArrayObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return ValidateDeleteVertexArraysBase(context, n); +} + +bool ValidateGenVertexArraysOES(Context *context, GLsizei n) +{ + if (!context->getExtensions().vertexArrayObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return ValidateGenVertexArraysBase(context, n); +} + +bool ValidateIsVertexArrayOES(Context *context) +{ + if (!context->getExtensions().vertexArrayObject) + { + context->recordError(Error(GL_INVALID_OPERATION, "Extension not enabled")); + return false; + } + + return true; +} } diff --git a/gfx/angle/src/libANGLE/validationES2.h b/gfx/angle/src/libANGLE/validationES2.h index 4a858d98eee2..44166af07748 100644 --- a/gfx/angle/src/libANGLE/validationES2.h +++ b/gfx/angle/src/libANGLE/validationES2.h @@ -33,6 +33,11 @@ bool ValidateDiscardFramebufferEXT(Context *context, GLenum target, GLsizei numA const GLenum *attachments); bool ValidateDrawBuffers(Context *context, GLsizei n, const GLenum *bufs); + +bool ValidateBindVertexArrayOES(Context *context, GLuint array); +bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n); +bool ValidateGenVertexArraysOES(Context *context, GLsizei n); +bool ValidateIsVertexArrayOES(Context *context); } #endif // LIBANGLE_VALIDATION_ES2_H_ diff --git a/gfx/angle/src/libANGLE/validationES3.cpp b/gfx/angle/src/libANGLE/validationES3.cpp index b13ee9c10c64..97dac8b29baa 100644 --- a/gfx/angle/src/libANGLE/validationES3.cpp +++ b/gfx/angle/src/libANGLE/validationES3.cpp @@ -195,33 +195,6 @@ ES3FormatCombinationSet BuildES3FormatSet() // From GL_ANGLE_depth_texture InsertES3FormatCombo(&set, GL_DEPTH_COMPONENT32_OES, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8_OES ); - // Compressed formats - // From ES 3.0.1 spec, table 3.16 - // | Internal format | Format | Type | - // | | | | - InsertES3FormatCombo(&set, GL_COMPRESSED_R11_EAC, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_R11_EAC, GL_COMPRESSED_R11_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_SIGNED_R11_EAC, GL_COMPRESSED_SIGNED_R11_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_RG11_EAC, GL_COMPRESSED_RG11_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_SIGNED_RG11_EAC, GL_COMPRESSED_SIGNED_RG11_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_RGB8_ETC2, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_SRGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_UNSIGNED_BYTE); - - - // From GL_EXT_texture_compression_dxt1 - InsertES3FormatCombo(&set, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE); - InsertES3FormatCombo(&set, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE); - - // From GL_ANGLE_texture_compression_dxt3 - InsertES3FormatCombo(&set, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_UNSIGNED_BYTE); - - // From GL_ANGLE_texture_compression_dxt5 - InsertES3FormatCombo(&set, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE, GL_UNSIGNED_BYTE); - return set; } @@ -406,6 +379,12 @@ bool ValidateES3TexImageParameters(Context *context, GLenum target, GLint level, return false; } + if (!actualFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions())) + { + context->recordError(Error(GL_INVALID_ENUM)); + return false; + } + if (target == GL_TEXTURE_3D) { context->recordError(Error(GL_INVALID_OPERATION)); @@ -1299,4 +1278,48 @@ bool ValidateCompressedTexImage3D(Context *context, return true; } + +bool ValidateBindVertexArray(Context *context, GLuint array) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateBindVertexArrayBase(context, array); +} + +bool ValidateDeleteVertexArrays(Context *context, GLsizei n) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateDeleteVertexArraysBase(context, n); +} + +bool ValidateGenVertexArrays(Context *context, GLsizei n) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return ValidateGenVertexArraysBase(context, n); +} + +bool ValidateIsVertexArray(Context *context) +{ + if (context->getClientVersion() < 3) + { + context->recordError(Error(GL_INVALID_OPERATION)); + return false; + } + + return true; +} } diff --git a/gfx/angle/src/libANGLE/validationES3.h b/gfx/angle/src/libANGLE/validationES3.h index 60f321e03d9a..c1d7b74f82bb 100644 --- a/gfx/angle/src/libANGLE/validationES3.h +++ b/gfx/angle/src/libANGLE/validationES3.h @@ -54,6 +54,11 @@ bool ValidateCompressedTexImage3D(Context *context, GLint border, GLsizei imageSize, const GLvoid *data); + +bool ValidateBindVertexArray(Context *context, GLuint array); +bool ValidateDeleteVertexArrays(Context *context, GLsizei n); +bool ValidateGenVertexArrays(Context *context, GLsizei n); +bool ValidateIsVertexArray(Context *context); } #endif // LIBANGLE_VALIDATION_ES3_H_ diff --git a/gfx/angle/src/libANGLE/validationES_unittest.cpp b/gfx/angle/src/libANGLE/validationES_unittest.cpp new file mode 100644 index 000000000000..1b4a640bc23b --- /dev/null +++ b/gfx/angle/src/libANGLE/validationES_unittest.cpp @@ -0,0 +1,145 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// validationES unit tests: +// Unit tests for general ES validation functions. +// + +#include +#include + +#include "libANGLE/Data.h" +#include "libANGLE/renderer/FramebufferImpl_mock.h" +#include "libANGLE/renderer/ProgramImpl_mock.h" +#include "libANGLE/renderer/TextureImpl_mock.h" +#include "libANGLE/validationES.h" +#include "tests/angle_unittests_utils.h" + +using namespace gl; +using namespace rx; +using testing::_; +using testing::Return; + +namespace +{ + +class MockFactory : public NullFactory +{ + public: + MOCK_METHOD1(createFramebuffer, FramebufferImpl *(const gl::Framebuffer::Data &)); + MOCK_METHOD1(createProgram, ProgramImpl *(const gl::Program::Data &)); + MOCK_METHOD1(createVertexArray, VertexArrayImpl *(const gl::VertexArray::Data &)); +}; + +class MockValidationContext : public ValidationContext +{ + public: + MockValidationContext(GLint clientVersion, + const State &state, + const Caps &caps, + const TextureCapsMap &textureCaps, + const Extensions &extensions, + const ResourceManager *resourceManager, + const Limitations &limitations); + + MOCK_METHOD1(recordError, void(const Error &)); +}; + +MockValidationContext::MockValidationContext(GLint clientVersion, + const State &state, + const Caps &caps, + const TextureCapsMap &textureCaps, + const Extensions &extensions, + const ResourceManager *resourceManager, + const Limitations &limitations) + : ValidationContext(clientVersion, + state, + caps, + textureCaps, + extensions, + resourceManager, + limitations) +{ +} + +// Test that ANGLE generates an INVALID_OPERATION when validating index data that uses a value +// larger than MAX_ELEMENT_INDEX. Not specified in the GLES 3 spec, it's undefined behaviour, +// but we want a test to ensure we maintain this behaviour. +TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) +{ + // TODO(jmadill): Generalize some of this code so we can re-use it for other tests. + MockFramebufferImpl *framebufferImpl = new MockFramebufferImpl(); + EXPECT_CALL(*framebufferImpl, onUpdateColorAttachment(_)).Times(1).RetiresOnSaturation(); + EXPECT_CALL(*framebufferImpl, checkStatus()) + .Times(2) + .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) + .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)); + EXPECT_CALL(*framebufferImpl, destroy()).Times(1).RetiresOnSaturation(); + + MockProgramImpl *programImpl = new MockProgramImpl(); + EXPECT_CALL(*programImpl, destroy()); + + MockFactory mockFactory; + EXPECT_CALL(mockFactory, createFramebuffer(_)).WillOnce(Return(framebufferImpl)); + EXPECT_CALL(mockFactory, createProgram(_)).WillOnce(Return(programImpl)); + EXPECT_CALL(mockFactory, createVertexArray(_)).WillOnce(Return(nullptr)); + + State state; + Caps caps; + TextureCapsMap textureCaps; + Extensions extensions; + Limitations limitations; + + // Set some basic caps. + caps.maxElementIndex = 100; + caps.maxDrawBuffers = 1; + caps.maxColorAttachments = 1; + state.initialize(caps, 3); + + MockTextureImpl *textureImpl = new MockTextureImpl(); + EXPECT_CALL(*textureImpl, setStorage(_, _, _, _)).WillOnce(Return(Error(GL_NO_ERROR))); + EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation(); + Texture *texture = new Texture(textureImpl, 0, GL_TEXTURE_2D); + texture->addRef(); + texture->setStorage(GL_TEXTURE_2D, 1, GL_RGBA8, Extents(1, 1, 0)); + + VertexArray *vertexArray = new VertexArray(&mockFactory, 0, 1); + Framebuffer *framebuffer = new Framebuffer(caps, &mockFactory, 1); + framebuffer->setAttachment(GL_FRAMEBUFFER_DEFAULT, GL_BACK, ImageIndex::Make2D(0), texture); + + Program *program = new Program(&mockFactory, nullptr, 1); + + state.setVertexArrayBinding(vertexArray); + state.setDrawFramebufferBinding(framebuffer); + state.setProgram(program); + + MockValidationContext testContext(3, state, caps, textureCaps, extensions, nullptr, + limitations); + + // Set the expectation for the validation error here. + Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage); + EXPECT_CALL(testContext, recordError(expectedError)).Times(1); + + // Call once with maximum index, and once with an excessive index. + GLuint indexData[] = {0, 1, static_cast(caps.maxElementIndex - 1), + 3, 4, static_cast(caps.maxElementIndex)}; + IndexRange indexRange; + EXPECT_TRUE(ValidateDrawElements(&testContext, GL_TRIANGLES, 3, GL_UNSIGNED_INT, indexData, 1, + &indexRange)); + EXPECT_FALSE(ValidateDrawElements(&testContext, GL_TRIANGLES, 6, GL_UNSIGNED_INT, indexData, 2, + &indexRange)); + + texture->release(); + + state.setVertexArrayBinding(nullptr); + state.setDrawFramebufferBinding(nullptr); + state.setProgram(nullptr); + + SafeDelete(vertexArray); + SafeDelete(framebuffer); + SafeDelete(program); +} + +} // anonymous namespace diff --git a/gfx/angle/src/libEGL/moz.build b/gfx/angle/src/libEGL/moz.build index 70b5fc4472bc..f4cd9ebffd79 100644 --- a/gfx/angle/src/libEGL/moz.build +++ b/gfx/angle/src/libEGL/moz.build @@ -66,8 +66,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: OS_LIBS += [ 'd3d9', 'dxguid' ] else: EXTRA_DSO_LDOPTS += [ - '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), - '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), ] diff --git a/gfx/angle/src/libGLESv2.gypi b/gfx/angle/src/libGLESv2.gypi index a59185945552..f4fcb313d647 100644 --- a/gfx/angle/src/libGLESv2.gypi +++ b/gfx/angle/src/libGLESv2.gypi @@ -200,6 +200,8 @@ 'libANGLE/renderer/d3d/loadimage.h', 'libANGLE/renderer/d3d/loadimage.inl', 'libANGLE/renderer/d3d/loadimageSSE2.cpp', + 'libANGLE/renderer/d3d/loadimage_etc.cpp', + 'libANGLE/renderer/d3d/loadimage_etc.h', 'libANGLE/renderer/d3d/ProgramD3D.cpp', 'libANGLE/renderer/d3d/ProgramD3D.h', 'libANGLE/renderer/d3d/RenderbufferD3D.cpp', @@ -221,6 +223,8 @@ 'libANGLE/renderer/d3d/TextureStorage.h', 'libANGLE/renderer/d3d/TransformFeedbackD3D.cpp', 'libANGLE/renderer/d3d/TransformFeedbackD3D.h', + 'libANGLE/renderer/d3d/VaryingPacking.cpp', + 'libANGLE/renderer/d3d/VaryingPacking.h', 'libANGLE/renderer/d3d/VertexBuffer.cpp', 'libANGLE/renderer/d3d/VertexBuffer.h', 'libANGLE/renderer/d3d/VertexDataManager.cpp', @@ -298,6 +302,10 @@ 'libANGLE/renderer/d3d/d3d11/IndexBuffer11.h', 'libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp', 'libANGLE/renderer/d3d/d3d11/InputLayoutCache.h', + 'libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.h', + 'libANGLE/renderer/d3d/d3d11/internal_format_initializer_table.cpp', + 'libANGLE/renderer/d3d/d3d11/load_functions_table.h', + 'libANGLE/renderer/d3d/d3d11/load_functions_table_autogen.cpp', 'libANGLE/renderer/d3d/d3d11/NativeWindow.h', 'libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp', 'libANGLE/renderer/d3d/d3d11/PixelTransfer11.h', @@ -365,18 +373,18 @@ 'libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2darrayps.h', 'libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2dps.h', 'libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui3dps.h', + 'libANGLE/renderer/d3d/d3d11/StateManager11.cpp', + 'libANGLE/renderer/d3d/d3d11/StateManager11.h', 'libANGLE/renderer/d3d/d3d11/SwapChain11.cpp', 'libANGLE/renderer/d3d/d3d11/SwapChain11.h', 'libANGLE/renderer/d3d/d3d11/swizzle_format_info.h', - 'libANGLE/renderer/d3d/d3d11/swizzle_format_info.cpp', + 'libANGLE/renderer/d3d/d3d11/swizzle_format_info_autogen.cpp', 'libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp', 'libANGLE/renderer/d3d/d3d11/TextureStorage11.h', 'libANGLE/renderer/d3d/d3d11/Trim11.cpp', 'libANGLE/renderer/d3d/d3d11/Trim11.h', - 'libANGLE/renderer/d3d/d3d11/texture_format_table.cpp', + 'libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp', 'libANGLE/renderer/d3d/d3d11/texture_format_table.h', - 'libANGLE/renderer/d3d/d3d11/texture_format_util.cpp', - 'libANGLE/renderer/d3d/d3d11/texture_format_util.h', 'libANGLE/renderer/d3d/d3d11/VertexArray11.h', 'libANGLE/renderer/d3d/d3d11/VertexBuffer11.cpp', 'libANGLE/renderer/d3d/d3d11/VertexBuffer11.h', @@ -780,10 +788,6 @@ }], ['angle_build_winrt==1', { - 'defines': - [ - 'NTDDI_VERSION=NTDDI_WINBLUE', - ], 'msvs_requires_importlibrary' : 'true', 'msvs_settings': { diff --git a/gfx/angle/src/libGLESv2/entry_points_egl.cpp b/gfx/angle/src/libGLESv2/entry_points_egl.cpp index 1b2397c9d501..8b5d7248f2ae 100644 --- a/gfx/angle/src/libGLESv2/entry_points_egl.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_egl.cpp @@ -49,14 +49,13 @@ EGLBoolean EGLAPIENTRY Initialize(EGLDisplay dpy, EGLint *major, EGLint *minor) EVENT("(EGLDisplay dpy = 0x%0.8p, EGLint *major = 0x%0.8p, EGLint *minor = 0x%0.8p)", dpy, major, minor); - if (dpy == EGL_NO_DISPLAY) + Display *display = static_cast(dpy); + if (dpy == EGL_NO_DISPLAY || !Display::isValidDisplay(display)) { SetGlobalError(Error(EGL_BAD_DISPLAY)); return EGL_FALSE; } - Display *display = static_cast(dpy); - Error error = display->initialize(); if (error.isError()) { @@ -75,13 +74,13 @@ EGLBoolean EGLAPIENTRY Terminate(EGLDisplay dpy) { EVENT("(EGLDisplay dpy = 0x%0.8p)", dpy); - if (dpy == EGL_NO_DISPLAY) + Display *display = static_cast(dpy); + if (dpy == EGL_NO_DISPLAY || !Display::isValidDisplay(display)) { SetGlobalError(Error(EGL_BAD_DISPLAY)); return EGL_FALSE; } - Display *display = static_cast(dpy); gl::Context *context = GetGlobalContext(); if (display->isValidContext(context)) @@ -505,15 +504,30 @@ EGLBoolean EGLAPIENTRY MakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface r Display *display = static_cast(dpy); gl::Context *context = static_cast(ctx); - bool noContext = (ctx == EGL_NO_CONTEXT); - bool noSurface = (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE); - if (noContext != noSurface) + // If ctx is EGL_NO_CONTEXT and either draw or read are not EGL_NO_SURFACE, an EGL_BAD_MATCH + // error is generated. + if (ctx == EGL_NO_CONTEXT && (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE)) { SetGlobalError(Error(EGL_BAD_MATCH)); return EGL_FALSE; } - if (dpy == EGL_NO_DISPLAY) + if (ctx != EGL_NO_CONTEXT && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE) + { + SetGlobalError(Error(EGL_BAD_MATCH)); + return EGL_FALSE; + } + + // If either of draw or read is a valid surface and the other is EGL_NO_SURFACE, an + // EGL_BAD_MATCH error is generated. + if ((read == EGL_NO_SURFACE) != (draw == EGL_NO_SURFACE)) + { + SetGlobalError(Error( + EGL_BAD_MATCH, "read and draw must both be valid surfaces, or both be EGL_NO_SURFACE")); + return EGL_FALSE; + } + + if (dpy == EGL_NO_DISPLAY || !Display::isValidDisplay(display)) { SetGlobalError(Error(EGL_BAD_DISPLAY, "'dpy' not a valid EGLDisplay handle")); return EGL_FALSE; @@ -1341,6 +1355,12 @@ __eglMustCastToProperFunctionPointerType EGLAPIENTRY GetProcAddress(const char * INSERT_PROC_ADDRESS(gl, EGLImageTargetTexture2DOES); INSERT_PROC_ADDRESS(gl, EGLImageTargetRenderbufferStorageOES); + // GL_OES_vertex_array_object + INSERT_PROC_ADDRESS(gl, BindVertexArrayOES); + INSERT_PROC_ADDRESS(gl, DeleteVertexArraysOES); + INSERT_PROC_ADDRESS(gl, GenVertexArraysOES); + INSERT_PROC_ADDRESS(gl, IsVertexArrayOES); + // GLES3 core INSERT_PROC_ADDRESS(gl, ReadBuffer); INSERT_PROC_ADDRESS(gl, DrawRangeElements); diff --git a/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp b/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp index 998bd1ffaef1..eb74fa3a91a0 100644 --- a/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp @@ -309,15 +309,8 @@ EGLBoolean EGLAPIENTRY QueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribut switch (attribute) { case EGL_D3D11_DEVICE_ANGLE: - if (!dev->getExtensions().deviceD3D || dev->getType() != EGL_D3D11_DEVICE_ANGLE) - { - SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); - return EGL_FALSE; - } - error = dev->getDevice(value); - break; case EGL_D3D9_DEVICE_ANGLE: - if (!dev->getExtensions().deviceD3D || dev->getType() != EGL_D3D9_DEVICE_ANGLE) + if (!dev->getExtensions().deviceD3D || dev->getType() != attribute) { SetGlobalError(Error(EGL_BAD_ATTRIBUTE)); return EGL_FALSE; diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp index a1a7222b7e17..2095bf245923 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp @@ -2246,7 +2246,7 @@ void GL_APIENTRY GetShaderiv(GLuint shader, GLenum pname, GLint* params) *params = shaderObject->getSourceLength(); return; case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE: - *params = shaderObject->getTranslatedSourceLength(); + *params = shaderObject->getTranslatedSourceWithDebugInfoLength(); return; default: diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp index bdd8b67b8ce6..1531ad0cabe0 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp @@ -390,7 +390,6 @@ void GL_APIENTRY GetTranslatedShaderSourceANGLE(GLuint shader, GLsizei bufsize, return; } - // Only returns extra info if ANGLE_GENERATE_SHADER_DEBUG_INFO is defined shaderObject->getTranslatedSourceWithDebugInfo(bufsize, length, source); } } @@ -1202,4 +1201,86 @@ ANGLE_EXPORT void GL_APIENTRY EGLImageTargetRenderbufferStorageOES(GLenum target } } } + +void GL_APIENTRY BindVertexArrayOES(GLuint array) +{ + EVENT("(GLuint array = %u)", array); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateBindVertexArrayOES(context, array)) + { + return; + } + + context->bindVertexArray(array); + } +} + +void GL_APIENTRY DeleteVertexArraysOES(GLsizei n, const GLuint *arrays) +{ + EVENT("(GLsizei n = %d, const GLuint* arrays = 0x%0.8p)", n, arrays); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateDeleteVertexArraysOES(context, n)) + { + return; + } + + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + if (arrays[arrayIndex] != 0) + { + context->deleteVertexArray(arrays[arrayIndex]); + } + } + } +} + +void GL_APIENTRY GenVertexArraysOES(GLsizei n, GLuint *arrays) +{ + EVENT("(GLsizei n = %d, GLuint* arrays = 0x%0.8p)", n, arrays); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateGenVertexArraysOES(context, n)) + { + return; + } + + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + arrays[arrayIndex] = context->createVertexArray(); + } + } +} + +GLboolean GL_APIENTRY IsVertexArrayOES(GLuint array) +{ + EVENT("(GLuint array = %u)", array); + + Context *context = GetValidGlobalContext(); + if (context) + { + if (!ValidateIsVertexArrayOES(context)) + { + return GL_FALSE; + } + + if (array == 0) + { + return GL_FALSE; + } + + VertexArray *vao = context->getVertexArray(array); + + return (vao != nullptr ? GL_TRUE : GL_FALSE); + } + + return GL_FALSE; +} } diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h index 08d576b75823..486df6a1991d 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h +++ b/gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.h @@ -85,6 +85,12 @@ ANGLE_EXPORT void GL_APIENTRY PopGroupMarkerEXT(); ANGLE_EXPORT void GL_APIENTRY EGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image); ANGLE_EXPORT void GL_APIENTRY EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image); + +// GL_OES_vertex_array_object +ANGLE_EXPORT void GL_APIENTRY BindVertexArrayOES(GLuint array); +ANGLE_EXPORT void GL_APIENTRY DeleteVertexArraysOES(GLsizei n, const GLuint *arrays); +ANGLE_EXPORT void GL_APIENTRY GenVertexArraysOES(GLsizei n, GLuint *arrays); +ANGLE_EXPORT GLboolean GL_APIENTRY IsVertexArrayOES(GLuint array); } #endif // LIBGLESV2_ENTRYPOINTGLES20EXT_H_ diff --git a/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp b/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp index b85010999366..2c19eb6d588b 100644 --- a/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp +++ b/gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp @@ -806,19 +806,8 @@ void GL_APIENTRY BindVertexArray(GLuint array) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateBindVertexArray(context, array)) { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - VertexArray *vao = context->getVertexArray(array); - - if (!vao) - { - // The default VAO should always exist - ASSERT(array != 0); - context->recordError(Error(GL_INVALID_OPERATION)); return; } @@ -833,15 +822,8 @@ void GL_APIENTRY DeleteVertexArrays(GLsizei n, const GLuint* arrays) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateDeleteVertexArrays(context, n)) { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); return; } @@ -862,15 +844,8 @@ void GL_APIENTRY GenVertexArrays(GLsizei n, GLuint* arrays) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateGenVertexArrays(context, n)) { - context->recordError(Error(GL_INVALID_OPERATION)); - return; - } - - if (n < 0) - { - context->recordError(Error(GL_INVALID_VALUE)); return; } @@ -888,9 +863,8 @@ GLboolean GL_APIENTRY IsVertexArray(GLuint array) Context *context = GetValidGlobalContext(); if (context) { - if (context->getClientVersion() < 3) + if (!ValidateIsVertexArray(context)) { - context->recordError(Error(GL_INVALID_OPERATION)); return GL_FALSE; } diff --git a/gfx/angle/src/libGLESv2/global_state.cpp b/gfx/angle/src/libGLESv2/global_state.cpp index 686f0bf49bc0..b99c3e1ca9ba 100644 --- a/gfx/angle/src/libGLESv2/global_state.cpp +++ b/gfx/angle/src/libGLESv2/global_state.cpp @@ -55,13 +55,6 @@ Current *AllocateCurrent() return current; } -void DeallocateCurrent() -{ - Current *current = reinterpret_cast(GetTLSValue(currentTLS)); - SafeDelete(current); - SetTLSValue(currentTLS, NULL); -} - Current *GetCurrentData() { // Create a TLS index if one has not been created for this DLL @@ -78,6 +71,14 @@ Current *GetCurrentData() } #ifdef ANGLE_PLATFORM_WINDOWS + +void DeallocateCurrent() +{ + Current *current = reinterpret_cast(GetTLSValue(currentTLS)); + SafeDelete(current); + SetTLSValue(currentTLS, NULL); +} + extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) { switch (reason) diff --git a/gfx/angle/src/libGLESv2/libGLESv2.cpp b/gfx/angle/src/libGLESv2/libGLESv2.cpp index 0d5dfd55c731..71eb25dd8aa6 100644 --- a/gfx/angle/src/libGLESv2/libGLESv2.cpp +++ b/gfx/angle/src/libGLESv2/libGLESv2.cpp @@ -1439,4 +1439,24 @@ void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImag { return gl::EGLImageTargetRenderbufferStorageOES(target, image); } + +void GL_APIENTRY glBindVertexArrayOES(GLuint array) +{ + return gl::BindVertexArrayOES(array); +} + +void GL_APIENTRY glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays) +{ + return gl::DeleteVertexArraysOES(n, arrays); +} + +void GL_APIENTRY glGenVertexArraysOES(GLsizei n, GLuint *arrays) +{ + return gl::GenVertexArraysOES(n, arrays); +} + +GLboolean GL_APIENTRY glIsVertexArrayOES(GLuint array) +{ + return gl::IsVertexArrayOES(array); +} } diff --git a/gfx/angle/src/libGLESv2/libGLESv2.def b/gfx/angle/src/libGLESv2/libGLESv2.def index bf5418ebd23c..6bba27042658 100644 --- a/gfx/angle/src/libGLESv2/libGLESv2.def +++ b/gfx/angle/src/libGLESv2/libGLESv2.def @@ -183,6 +183,10 @@ EXPORTS glPopGroupMarkerEXT @296 glEGLImageTargetTexture2DOES @297 glEGLImageTargetRenderbufferStorageOES @298 + glBindVertexArrayOES @299 + glDeleteVertexArraysOES @300 + glGenVertexArraysOES @301 + glIsVertexArrayOES @302 ; GLES 3.0 Functions glReadBuffer @180 diff --git a/gfx/angle/src/libGLESv2/moz.build b/gfx/angle/src/libGLESv2/moz.build index 2881257fc056..561b266f4845 100644 --- a/gfx/angle/src/libGLESv2/moz.build +++ b/gfx/angle/src/libGLESv2/moz.build @@ -71,8 +71,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: OS_LIBS += [ 'd3d9', 'dxguid' ] else: EXTRA_DSO_LDOPTS += [ - '%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), - '%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), + '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), ] GeckoSharedLibrary('libGLESv2', linkage=None) diff --git a/gfx/angle/src/tests/BUILD.gn b/gfx/angle/src/tests/BUILD.gn index 8c3191f6327f..f95a76e06f71 100644 --- a/gfx/angle/src/tests/BUILD.gn +++ b/gfx/angle/src/tests/BUILD.gn @@ -40,86 +40,88 @@ test("angle_unittests") { ] } -end2end_gypi = exec_script("//build/gypi_to_gn.py", - [ - rebase_path("angle_end2end_tests.gypi"), - "--replace=<(angle_path)=.", - ], - "scope", - [ "angle_end2end_tests.gypi" ]) - -test("angle_end2end_tests") { - include_dirs = [ - "testing/gtest/include", - "../../src/tests", - "../../util", - ] - - sources = rebase_path(end2end_gypi.angle_end2end_tests_sources, ".", "../..") - - if (is_win) { - sources += - rebase_path(end2end_gypi.angle_end2end_tests_win_sources, ".", "../..") - } - - sources += [ "//gpu/angle_end2end_tests_main.cc" ] - - configs += [ - "//third_party/angle:internal_config", - "//third_party/angle:libANGLE_config", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", - "//third_party/angle:angle_util", - "//third_party/angle:libANGLE", - "//third_party/angle:libEGL", - "//third_party/angle:libGLESv2", - "//third_party/angle:preprocessor", - "//third_party/angle:translator_static", - ] -} - -perftests_gypi = exec_script("//build/gypi_to_gn.py", +if (is_win || is_linux || is_mac) { + end2end_gypi = exec_script("//build/gypi_to_gn.py", [ - rebase_path("angle_perftests.gypi"), + rebase_path("angle_end2end_tests.gypi"), "--replace=<(angle_path)=.", ], "scope", - [ "angle_perftests.gypi" ]) + [ "angle_end2end_tests.gypi" ]) -test("angle_perftests") { - include_dirs = [ - "testing/gtest/include", - "../../src/tests", - "../../util", - ] + test("angle_end2end_tests") { + include_dirs = [ + "testing/gtest/include", + "../../src/tests", + "../../util", + ] - sources = rebase_path(perftests_gypi.angle_perf_tests_sources, ".", "../..") + sources = rebase_path(end2end_gypi.angle_end2end_tests_sources, ".", "../..") - if (is_win) { - sources += - rebase_path(perftests_gypi.angle_perf_tests_win_sources, ".", "../..") + if (is_win) { + sources += + rebase_path(end2end_gypi.angle_end2end_tests_win_sources, ".", "../..") + } + + sources += [ "//gpu/angle_end2end_tests_main.cc" ] + + configs += [ + "//third_party/angle:internal_config", + "//third_party/angle:libANGLE_config", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + "//third_party/angle:angle_util", + "//third_party/angle:libANGLE", + "//third_party/angle:libEGL", + "//third_party/angle:libGLESv2", + "//third_party/angle:preprocessor", + "//third_party/angle:translator_static", + ] + } +} + +if (is_win) { + perftests_gypi = exec_script("//build/gypi_to_gn.py", + [ + rebase_path("angle_perftests.gypi"), + "--replace=<(angle_path)=.", + ], + "scope", + [ "angle_perftests.gypi" ]) + + test("angle_perftests") { + include_dirs = [ + "testing/gtest/include", + "../../src/tests", + "../../util", + ] + + sources = rebase_path(perftests_gypi.angle_perf_tests_sources, ".", "../..") + + sources += + rebase_path(perftests_gypi.angle_perf_tests_win_sources, ".", "../..") + + sources += [ "//gpu/angle_perftests_main.cc" ] + + configs += [ + "//third_party/angle:internal_config", + "//third_party/angle:libANGLE_config", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + "//third_party/angle:angle_util", + "//third_party/angle:libANGLE", + "//third_party/angle:libEGL", + "//third_party/angle:libGLESv2", + ] } - - sources += [ "//gpu/angle_perftests_main.cc" ] - - configs += [ - "//third_party/angle:internal_config", - "//third_party/angle:libANGLE_config", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", - "//third_party/angle:angle_util", - "//third_party/angle:libANGLE", - "//third_party/angle:libEGL", - "//third_party/angle:libGLESv2", - ] } diff --git a/gfx/angle/src/tests/WebGL-CTS-known-failures.txt b/gfx/angle/src/tests/WebGL-CTS-known-failures.txt index 937fdbd08dbf..c67f3a291288 100644 --- a/gfx/angle/src/tests/WebGL-CTS-known-failures.txt +++ b/gfx/angle/src/tests/WebGL-CTS-known-failures.txt @@ -1,9 +1,9 @@ -Known failures in the WebGL CTS 1.0.4, using Canary 46.0.2457.0 with ANGLE 02df796f466c +Known failures in the WebGL CTS 1.0.4, using Canary 48.0.2547.0 with ANGLE a33475b36f7b0 https://www.khronos.org/registry/webgl/sdk/tests/webgl-conformance-tests.html +conformance/extensions/ext-sRGB.html: 5 tests failed conformance/glsl/bugs/pow-of-small-constant-in-user-defined-function.html: 1 tests failed conformance/glsl/bugs/sampler-struct-function-arg.html: 1 tests failed -conformance/misc/expando-loss.html: 2 tests failed +conformance/textures/misc/cube-incomplete-fbo.html: 1 tests failed deqp/data/gles2/shaders/functions.html: 8 tests failed -deqp/data/gles2/shaders/preprocessor.html: 21 tests failed deqp/data/gles2/shaders/scoping.html: 4 tests failed diff --git a/gfx/angle/src/tests/angle_end2end_tests.gypi b/gfx/angle/src/tests/angle_end2end_tests.gypi index affa5633977e..28ff23585a0c 100644 --- a/gfx/angle/src/tests/angle_end2end_tests.gypi +++ b/gfx/angle/src/tests/angle_end2end_tests.gypi @@ -44,6 +44,7 @@ '<(angle_path)/src/tests/gl_tests/PbufferTest.cpp', '<(angle_path)/src/tests/gl_tests/PBOExtensionTest.cpp', '<(angle_path)/src/tests/gl_tests/PointSpritesTest.cpp', + '<(angle_path)/src/tests/gl_tests/ProvokingVertexTest.cpp', '<(angle_path)/src/tests/gl_tests/OcclusionQueriesTest.cpp', '<(angle_path)/src/tests/gl_tests/ProgramBinaryTest.cpp', '<(angle_path)/src/tests/gl_tests/ReadPixelsTest.cpp', diff --git a/gfx/angle/src/tests/angle_perftests.gypi b/gfx/angle/src/tests/angle_perftests.gypi index 5658d80598d9..16bc990692ad 100644 --- a/gfx/angle/src/tests/angle_perftests.gypi +++ b/gfx/angle/src/tests/angle_perftests.gypi @@ -21,6 +21,7 @@ '<(angle_path)/src/tests/perf_tests/DrawCallPerf.cpp', '<(angle_path)/src/tests/perf_tests/EGLInitializePerf.cpp', '<(angle_path)/src/tests/perf_tests/IndexConversionPerf.cpp', + '<(angle_path)/src/tests/perf_tests/InterleavedAttributeData.cpp', '<(angle_path)/src/tests/perf_tests/PointSprites.cpp', '<(angle_path)/src/tests/perf_tests/TexSubImage.cpp', '<(angle_path)/src/tests/perf_tests/third_party/perf/perf_test.cc', diff --git a/gfx/angle/src/tests/angle_unittests.gypi b/gfx/angle/src/tests/angle_unittests.gypi index af33d67ff011..bd7253bc0237 100644 --- a/gfx/angle/src/tests/angle_unittests.gypi +++ b/gfx/angle/src/tests/angle_unittests.gypi @@ -29,7 +29,10 @@ '<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp', '<(angle_path)/src/libANGLE/Surface_unittest.cpp', '<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp', + '<(angle_path)/src/libANGLE/validationES_unittest.cpp', '<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h', + '<(angle_path)/src/libANGLE/renderer/FramebufferImpl_mock.h', + '<(angle_path)/src/libANGLE/renderer/ProgramImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/RenderbufferImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/ImageImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/TextureImpl_mock.h', @@ -80,7 +83,6 @@ # TODO(jmadill): should probably call this windows sources 'angle_unittests_hlsl_sources': [ - '<(angle_path)/src/libANGLE/renderer/d3d/DynamicHLSL_unittest.cpp', '<(angle_path)/src/tests/compiler_tests/UnrollFlatten_test.cpp', ], }, diff --git a/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp b/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp index 194a5b8aca5a..e1ff02c3e7d2 100644 --- a/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/ConstantFolding_test.cpp @@ -71,12 +71,19 @@ class ConstantFinder : public TIntermTraverser bool isEqual(const TConstantUnion &node, const int &value) const { - return mFaultTolerance >= abs(node.getIConst() - value); + ASSERT(mFaultTolerance < std::numeric_limits::max()); + // abs() returns 0 at least on some platforms when the minimum int value is passed in (it + // doesn't have a positive counterpart). + return mFaultTolerance >= abs(node.getIConst() - value) && + (node.getIConst() - value) != std::numeric_limits::min(); } bool isEqual(const TConstantUnion &node, const unsigned int &value) const { - return mFaultTolerance >= abs(static_cast(node.getUConst() - value)); + ASSERT(mFaultTolerance < static_cast(std::numeric_limits::max())); + return static_cast(mFaultTolerance) >= + abs(static_cast(node.getUConst() - value)) && + static_cast(node.getUConst() - value) != std::numeric_limits::min(); } bool isEqual(const TConstantUnion &node, const bool &value) const @@ -482,3 +489,258 @@ TEST_F(ConstantFoldingTest, Fold3x3MatrixTranspose) ASSERT_TRUE(constantVectorFoundInAST(result)); } +// Test that 0xFFFFFFFF wraps to -1 when parsed as integer. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, ParseWrappedHexIntLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform int inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const int i = 0xFFFFFFFF;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-1)); +} + +// Test that 3000000000 wraps to -1294967296 when parsed as integer. +// This is featured in the examples of GLSL 4.5, and ESSL behavior should match +// desktop GLSL when it comes to integer parsing. +TEST_F(ConstantFoldingTest, ParseWrappedDecimalIntLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform int inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const int i = 3000000000;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(-1294967296)); +} + +// Test that 0xFFFFFFFFu is parsed correctly as an unsigned integer literal. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, ParseMaxUintLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform uint inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const uint i = 0xFFFFFFFFu;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu)); +} + +// Test that unary minus applied to unsigned int is constant folded correctly. +// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42 +// means that any 32-bit unsigned integer value is a valid literal. +TEST_F(ConstantFoldingTest, FoldUnaryMinusOnUintLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "uniform uint inInt;\n" + "out vec4 my_Vec;\n" + "void main() {\n" + " const uint i = -1u;\n" + " my_Vec = vec4(i * inInt);\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0xFFFFFFFFu)); +} + +// Test that constant mat2 initialization with a mat2 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat2) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(mat2(0.0, 1.0, 2.0, 3.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 2.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that constant mat2 initialization with an int parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingScalar) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(3);\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 3.0f, 0.0f, + 0.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that constant mat2 initialization with a mix of parameters works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMix) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(-1, vec2(0.0, 1.0), vec4(2.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + -1.0, 0.0f, + 1.0f, 2.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that constant mat2 initialization with a mat3 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingMat3) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(mat3(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 3.0f, 4.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that constant mat4x3 initialization with a mat3x2 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat4x3ConstructorTakingMat3x2) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "uniform float mult;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " const mat4x3 cm = mat4x3(mat3x2(1.0, 2.0,\n" + " 3.0, 4.0,\n" + " 5.0, 6.0));\n" + " mat4x3 m = cm * mult;\n" + " my_FragColor = vec4(m[0], m[1][0]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 1.0f, 2.0f, 0.0f, + 3.0f, 4.0f, 0.0f, + 5.0f, 6.0f, 1.0f, + 0.0f, 0.0f, 0.0f + }; + std::vector result(outputElements, outputElements + 12); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + + +// Test that constant mat2 initialization with a vec4 parameter works correctly. +TEST_F(ConstantFoldingTest, FoldMat2ConstructorTakingVec4) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float mult;\n" + "void main() {\n" + " const mat2 cm = mat2(vec4(0.0, 1.0, 2.0, 3.0));\n" + " mat2 m = cm * mult;\n" + " gl_FragColor = vec4(m[0], m[1]);\n" + "}\n"; + compile(shaderString); + float outputElements[] = + { + 0.0f, 1.0f, + 2.0f, 3.0f + }; + std::vector result(outputElements, outputElements + 4); + ASSERT_TRUE(constantVectorFoundInAST(result)); +} + +// Test that equality comparison of two different structs with a nested struct inside returns false. +TEST_F(ConstantFoldingTest, FoldNestedDifferentStructEqualityComparison) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct nested {\n" + " float f\n;" + "};\n" + "struct S {\n" + " nested a;\n" + " float f;\n" + "};\n" + "uniform vec4 mult;\n" + "void main()\n" + "{\n" + " const S s1 = S(nested(0.0), 2.0);\n" + " const S s2 = S(nested(0.0), 3.0);\n" + " gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(0.5f)); +} + +// Test that equality comparison of two identical structs with a nested struct inside returns true. +TEST_F(ConstantFoldingTest, FoldNestedIdenticalStructEqualityComparison) +{ + const std::string &shaderString = + "precision mediump float;\n" + "struct nested {\n" + " float f\n;" + "};\n" + "struct S {\n" + " nested a;\n" + " float f;\n" + " int i;\n" + "};\n" + "uniform vec4 mult;\n" + "void main()\n" + "{\n" + " const S s1 = S(nested(0.0), 2.0, 3);\n" + " const S s2 = S(nested(0.0), 2.0, 3);\n" + " gl_FragColor = (s1 == s2 ? 1.0 : 0.5) * mult;\n" + "}\n"; + compile(shaderString); + ASSERT_TRUE(constantFoundInAST(1.0f)); +} diff --git a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp index 263bd4fb7ab0..6e6d06c7376a 100644 --- a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp +++ b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp @@ -68,6 +68,22 @@ class MalformedVertexShaderTest : public MalformedShaderTest } }; +class MalformedWebGL2ShaderTest : public MalformedShaderTest +{ + public: + MalformedWebGL2ShaderTest() {} + + protected: + void SetUp() override + { + ShBuiltInResources resources; + ShInitBuiltInResources(&resources); + + mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_WEBGL2_SPEC); + ASSERT_TRUE(mTranslator->Init(resources)); + } +}; + // This is a test for a bug that used to exist in ANGLE: // Calling a function with all parameters missing should not succeed. TEST_F(MalformedShaderTest, FunctionParameterMismatch) @@ -916,3 +932,386 @@ TEST_F(MalformedShaderTest, ArrayValueFromFunctionParameterAsParameter) FAIL() << "Shader compilation failed, expecting success " << mInfoLog; } } + +// Test that out-of-range integer literal generates an error in ESSL 3.00. +TEST_F(MalformedShaderTest, OutOfRangeIntegerLiteral) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "precision highp int;\n" + "out vec4 my_FragColor;\n" + "void main() {\n" + " my_FragColor = vec4(0x100000000);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that vector field selection from a value taken from an array constructor is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, FieldSelectionFromVectorArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = vec2[1](vec2(0.0, 1.0))[0].x;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that structure field selection from a value taken from an array constructor is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, FieldSelectionFromStructArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "struct S { float member; };\n" + "void main()\n" + "{\n" + " const float f = S[1](S(0.0))[0].member;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a reference to a const array is accepted as a constant expression. +TEST_F(MalformedShaderTest, ArraySymbolIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float[2] arr = float[2](0.0, 1.0);\n" + " const float f = arr[0];\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a built-in function is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, BuiltInFunctionAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = sin(float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a built-in function is accepted as a +// constant expression. +TEST_F(MalformedShaderTest, BuiltInFunctionWithMultipleParametersAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = pow(1.0, float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in a parameter to a constructor is accepted as a constant +// expression. +TEST_F(MalformedShaderTest, ConstructorWithMultipleParametersAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const vec2 f = vec2(1.0, float[2](0.0, 1.0)[0]);\n" + " my_FragColor = vec4(f.x);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that using an array constructor in an operand of the ternary selection operator is accepted +// as a constant expression. +TEST_F(MalformedShaderTest, TernaryOperatorAppliedToArrayConstructorIsConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " const float f = true ? float[2](0.0, 1.0)[0] : 1.0;\n" + " my_FragColor = vec4(f);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a ternary operator with one unevaluated non-constant operand is not a constant +// expression. +TEST_F(MalformedShaderTest, TernaryOperatorNonConstantOperand) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform float u;\n" + "void main()\n" + "{\n" + " const float f = true ? 1.0 : u;\n" + " gl_FragColor = vec4(f);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a sampler can't be used in constructor argument list +TEST_F(MalformedShaderTest, SamplerInConstructorArguments) +{ + const std::string &shaderString = + "precision mediump float;\n" + "uniform sampler2D s;\n" + "void main()\n" + "{\n" + " vec2 v = vec2(0.0, s);\n" + " gl_FragColor = vec4(v, 0.0, 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that void can't be used in constructor argument list +TEST_F(MalformedShaderTest, VoidInConstructorArguments) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void foo() {}\n" + "void main()\n" + "{\n" + " vec2 v = vec2(0.0, foo());\n" + " gl_FragColor = vec4(v, 0.0, 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a shader passing a struct into a constructor of array of structs with 1 element works. +TEST_F(MalformedShaderTest, SingleStructArrayConstructor) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform float u;\n" + "struct S { float member; };\n" + "void main()\n" + "{\n" + " S[1] sarr = S[1](S(u));\n" + " my_FragColor = vec4(sarr[0].member);\n" + "}\n"; + if (!compile(shaderString)) + { + FAIL() << "Shader compilation failed, expecting success " << mInfoLog; + } +} + +// Test that a shader with empty constructor parameter list is not accepted. +TEST_F(MalformedShaderTest, EmptyArrayConstructor) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform float u;\n" + "const float[] f = f[]();\n" + "void main()\n" + "{\n" + " my_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing fragment outputs with a non-constant expression is forbidden, even if ANGLE +// is able to constant fold the index expression. ESSL 3.00 section 4.3.6. +TEST_F(MalformedShaderTest, DynamicallyIndexedFragmentOutput) +{ + const std::string &shaderString = + "#version 300 es" + "precision mediump float;\n" + "uniform int a;\n" + "out vec4[2] my_FragData;\n" + "void main()\n" + "{\n" + " my_FragData[true ? 0 : a] = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing an interface block array with a non-constant expression is forbidden, even if +// ANGLE is able to constant fold the index expression. ESSL 3.00 section 4.3.7. +TEST_F(MalformedShaderTest, DynamicallyIndexedInterfaceBlock) +{ + const std::string &shaderString = + "#version 300 es" + "precision mediump float;\n" + "uniform int a;\n" + "uniform B\n" + "{\n" + " vec4 f;\n" + "}\n" + "blocks[2];\n" + "out vec4 my_FragColor;\n" + "void main()\n" + "{\n" + " my_FragColor = blocks[true ? 0 : a].f;\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a shader that uses a struct definition in place of a struct constructor does not +// compile. See GLSL ES 1.00 section 5.4.3. +TEST_F(MalformedShaderTest, StructConstructorWithStructDefinition) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " struct s { float f; } (0.0);\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that indexing gl_FragData with a non-constant expression is forbidden in WebGL 2.0, even +// when ANGLE is able to constant fold the index. +// WebGL 2.0 spec section 'GLSL ES 1.00 Fragment Shader Output' +TEST_F(MalformedWebGL2ShaderTest, IndexFragDataWithNonConstant) +{ + const std::string &shaderString = + "precision mediump float;\n" + "void main()\n" + "{\n" + " for (int i = 0; i < 2; ++i) {\n" + " gl_FragData[true ? 0 : i] = vec4(0.0);\n" + " }\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a non-constant texture offset is not accepted for textureOffset. +// ESSL 3.00 section 8.8 +TEST_F(MalformedShaderTest, TextureOffsetNonConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec3 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "uniform int x;\n" + "void main()\n" + "{\n" + " my_FragColor = textureOffset(u_sampler, u_texCoord, ivec3(x, 3, -8));\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that a non-constant texture offset is not accepted for textureProjOffset with bias. +// ESSL 3.00 section 8.8 +TEST_F(MalformedShaderTest, TextureProjOffsetNonConst) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec4 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "uniform int x;\n" + "void main()\n" + "{\n" + " my_FragColor = textureProjOffset(u_sampler, u_texCoord, ivec3(x, 3, -8), 0.0);\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} + +// Test that an out-of-range texture offset is not accepted. +// GLES 3.0.4 section 3.8.10 specifies that out-of-range offset has undefined behavior. +TEST_F(MalformedShaderTest, TextureLodOffsetOutOfRange) +{ + const std::string &shaderString = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 my_FragColor;\n" + "uniform vec3 u_texCoord;\n" + "uniform mediump sampler3D u_sampler;\n" + "void main()\n" + "{\n" + " my_FragColor = textureLodOffset(u_sampler, u_texCoord, 0.0, ivec3(0, 0, 8));\n" + "}\n"; + if (compile(shaderString)) + { + FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; + } +} diff --git a/gfx/angle/src/tests/deqp_support/angle_deqp_gtest.cpp b/gfx/angle/src/tests/deqp_support/angle_deqp_gtest.cpp index da4e45e4121d..548c87841589 100644 --- a/gfx/angle/src/tests/deqp_support/angle_deqp_gtest.cpp +++ b/gfx/angle/src/tests/deqp_support/angle_deqp_gtest.cpp @@ -217,8 +217,6 @@ class dEQP_EGL : public dEQPTest<2> }; // TODO(jmadill): add different platform configs, or ability to choose platform -#if defined(ANGLE_STANDALONE_BUILD) - #define ANGLE_INSTANTIATE_DEQP_TEST_CASE(DEQP_TEST) \ TEST_P(DEQP_TEST, Default) { runTest(); } \ \ @@ -228,14 +226,6 @@ class dEQP_EGL : public dEQPTest<2> return DEQP_TEST::GetCaseGTestName(info.param); \ }) -#else // defined(ANGLE_STANDALONE_BUILD) - -#define ANGLE_INSTANTIATE_DEQP_TEST_CASE(DEQP_TEST) \ - TEST_P(DEQP_TEST, Default) { runTest(); } \ - INSTANTIATE_TEST_CASE_P(, DEQP_TEST, DEQP_TEST::GetTestingRange()) - -#endif // defined(ANGLE_STANDALONE_BUILD) - #ifdef ANGLE_DEQP_GLES2_TESTS ANGLE_INSTANTIATE_DEQP_TEST_CASE(dEQP_GLES2); #endif diff --git a/gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp b/gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp index 5e70a3be8153..df9add1fe269 100644 --- a/gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp +++ b/gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp @@ -48,7 +48,8 @@ const char *g_dEQPDataSearchDirs[] = "../third_party/deqp/src/data", "deqp_support/data", "third_party/deqp/src/data", - "../../third_party/deqp/src/data" + "../../third_party/deqp/src/data", + "../../../third_party/deqp/src/data" }; // TODO(jmadill): upstream to dEQP? diff --git a/gfx/angle/src/tests/deqp_support/deqp_egl_test_expectations.txt b/gfx/angle/src/tests/deqp_support/deqp_egl_test_expectations.txt index f0bb594f7fe0..fcd80fc83298 100644 --- a/gfx/angle/src/tests/deqp_support/deqp_egl_test_expectations.txt +++ b/gfx/angle/src/tests/deqp_support/deqp_egl_test_expectations.txt @@ -17,8 +17,6 @@ // TEST_NAME can be a specific test name, or have a '*' in the end, which // indicates a prefix matching. // -// Any tests whose expectations are not PASS will be skipped. -// // Examples: // 91530 MAC WIN LINUX : context_lost_restored = TIMEOUT // 91533 WIN : gl_min_uniforms = FAIL diff --git a/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt b/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt index 305a482f342f..5a4c823cd37d 100644 --- a/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt +++ b/gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt @@ -17,8 +17,6 @@ // TEST_NAME can be a specific test name, or have a '*' in the end, which // indicates a prefix matching. // -// Any tests whose expectations are not PASS will be skipped. -// // Examples: // 91530 MAC WIN LINUX : context_lost_restored = TIMEOUT // 91533 WIN : gl_min_uniforms = FAIL @@ -190,6 +188,8 @@ 1034 LINUX : dEQP-GLES2.functional.flush_finish.finish = FAIL 1019 LINUX : dEQP-GLES2.functional.texture.size.cube.256x256_rgba4444 = FAIL 1019 LINUX : dEQP-GLES2.functional.texture.size.cube.512x512_rgba4444 = FAIL +1020 LINUX : dEQP-GLES2.functional.texture.mipmap.2d.projected.nearest_linear* = FAIL +1020 LINUX : dEQP-GLES2.functional.texture.mipmap.2d.projected.linear_linear* = FAIL // Failures on the Linux dEQP bot that do not appear on the workstation 1143 LINUX : dEQP-GLES2.functional.shaders.random.all_features.vertex.13 = FAIL @@ -245,10 +245,10 @@ 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.valid.local_variable_hides_function_parameter_fragment = FAIL 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.invalid.redeclare_function_vertex = FAIL 1016 WIN LINUX MAC : dEQP-GLES2.functional.shaders.scoping.invalid.redeclare_function_fragment = FAIL -1020 WIN LINUX MAC : dEQP-GLES2.functional.texture.mipmap.2d.projected.* = FAIL +1020 WIN LINUX MAC : dEQP-GLES2.functional.texture.mipmap.2d.projected.nearest_nearest* = FAIL +1020 WIN LINUX MAC : dEQP-GLES2.functional.texture.mipmap.2d.projected.linear_nearest* = FAIL 1025 WIN LINUX MAC : dEQP-GLES2.functional.fragment_ops.random.* = FAIL 1025 WIN LINUX MAC : dEQP-GLES2.functional.fragment_ops.interaction.basic_shader.* = FAIL -1026 WIN LINUX MAC : dEQP-GLES2.functional.fbo.api.valid_texcube_attachments = FAIL 1027 WIN LINUX MAC : dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb = FAIL 1027 WIN LINUX MAC : dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgba = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.renderbuffer.color0.r16f = FAIL diff --git a/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt b/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt index 57bd4f353e8e..e8f98b2e97b9 100644 --- a/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt +++ b/gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt @@ -17,8 +17,6 @@ // TEST_NAME can be a specific test name, or have a '*' in the end, which // indicates a prefix matching. // -// Any tests whose expectations are not PASS will be skipped. -// // Examples: // 91530 MAC WIN LINUX : context_lost_restored = TIMEOUT // 91533 WIN : gl_min_uniforms = FAIL @@ -40,14 +38,6 @@ // TODO(jmadill): triage these into permanent and temporary failures -1087 WIN : dEQP-GLES3.functional.implementation_limits.num_compressed_texture_formats = FAIL -1087 WIN : dEQP-GLES3.functional.implementation_limits.compressed_texture_formats = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.varying.rules.invalid_type_struct_array = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.varying.rules.invalid_type_struct_struct = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.varying.rules.invalid_type_array_struct = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_vec3 = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_uvec2_vec3 = FAIL -1088 WIN : dEQP-GLES3.functional.shaders.linkage.uniform.block.differing_precision = FAIL 1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.local_function_prototype_vertex = FAIL 1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.local_function_prototype_fragment = FAIL 1089 WIN : dEQP-GLES3.functional.shaders.functions.invalid.overload_builtin_function_vertex = FAIL @@ -83,10 +73,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojoffset.sampler3d_float_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.isamplercube_* = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.usamplercube_* = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.isampler2darray_* = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.usampler2darray_* = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.isampler3d_* = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.usampler3d_* = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelod.sampler2dshadow_* = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler2d_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.isampler2d_fragment = FAIL @@ -102,10 +88,6 @@ 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.usampler3d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.sampler2dshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.texturelodoffset.sampler2dshadow_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.isampler3d_vertex = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.isampler3d_fragment = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.usampler3d_vertex = FAIL -1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.usampler3d_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.sampler2dshadow_vertex = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlod.sampler2dshadow_fragment = FAIL 1092 WIN : dEQP-GLES3.functional.shaders.texture_functions.textureprojlodoffset.isampler2d_vec3_vertex = FAIL @@ -262,12 +244,6 @@ 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.modf.highp_fragment = FAIL 1094 WIN : dEQP-GLES3.functional.shaders.invariance.highp.loop_2 = FAIL 1094 WIN : dEQP-GLES3.functional.shaders.invariance.mediump.loop_2 = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_size_vertex = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_size_fragment = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_length_vertex = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_length_fragment = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_vertex = FAIL -1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_fragment = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_clamp = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_repeat = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_mirror = FAIL @@ -280,16 +256,12 @@ 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.base_level.linear_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.base_level.nearest_linear = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.base_level.linear_linear = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.cube.base_level.nearest_nearest = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.cube.base_level.linear_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.cube.base_level.nearest_linear = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.cube.base_level.linear_linear = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.min_lod.nearest_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.min_lod.linear_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.nearest_nearest = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.linear_nearest = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.nearest_linear = FAIL -1095 WIN : dEQP-GLES3.functional.texture.mipmap.3d.base_level.linear_linear = FAIL 1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.luminance_all_zero = FAIL 1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.luminance_alpha_all_zero = FAIL 1095 WIN : dEQP-GLES3.functional.texture.swizzle.multi_channel.rg_all_zero = FAIL @@ -370,6 +342,8 @@ 1095 WIN : dEQP-GLES3.functional.texture.specification.basic_copytexsubimage2d.cube_rgba = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth.depth_component32f_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth.depth32f_stencil8_2d_array = FAIL +1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth_pbo.depth_component32f_2d_array = FAIL +1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth_pbo.depth32f_stencil8_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.teximage3d_depth_pbo.depth_component24_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage3d_depth.depth_component32f_2d_array = FAIL 1095 WIN : dEQP-GLES3.functional.texture.specification.texsubimage3d_depth.depth32f_stencil8_2d_array = FAIL @@ -604,7 +578,6 @@ 1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba16i = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba16ui = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.color.tex2darray.rgba8i = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba32i = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba16ui = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.tex3d.rgba8i = FAIL @@ -632,11 +605,6 @@ 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.rg16f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.color.repeated_clear.sample.tex2d.r16f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_write_clamp.depth_component32f = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_write_clamp.depth_component24 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_write_clamp.depth_component16 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_write_clamp.depth32f_stencil8 = FAIL -1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_write_clamp.depth24_stencil8 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component32f = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component24 = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.depth.depth_test_clamp.depth_component16 = FAIL @@ -957,107 +925,6 @@ 1099 WIN : dEQP-GLES3.functional.attribute_location.bind_relink_hole.mat2x2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_relink_hole.mat2 = FAIL 1099 WIN : dEQP-GLES3.functional.attribute_location.mixed_relink_hole.mat2x2 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.basic.int.* = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.basic.uint.* = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.array.int.* = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.array.uint.* = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.0 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.1 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.2 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.4 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.5 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.6 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.7 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.8 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.9 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.10 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.11 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.12 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.13 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.15 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.16 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.17 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.18 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.19 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.20 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.21 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.22 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.24 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.25 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.27 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.28 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.29 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.30 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.31 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.32 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.33 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.34 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.35 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.36 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.37 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.38 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.39 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.40 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.41 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.42 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.43 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.44 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.45 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.47 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.49 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.50 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.51 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.52 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.53 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.54 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.55 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.56 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.57 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.58 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.59 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.60 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.61 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.62 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.63 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.64 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.66 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.67 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.68 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.69 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.70 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.71 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.72 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.75 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.76 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.77 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.78 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.79 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.80 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.82 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.83 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.84 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.85 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.86 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.87 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.88 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.89 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.90 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.91 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.92 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.93 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.94 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.95 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.96 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.97 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.98 = FAIL -1100 WIN : dEQP-GLES3.functional.fragment_out.random.99 = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.triangles = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.triangle_strip = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.triangle_fan = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.lines = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.line_strip = FAIL -1101 WIN : dEQP-GLES3.functional.rasterization.flatshading.line_loop = FAIL -597 WIN : dEQP-GLES3.functional.primitive_restart.* = FAIL 1101 WIN : dEQP-GLES3.functional.rasterizer_discard.basic.clear_color = FAIL 1101 WIN : dEQP-GLES3.functional.rasterizer_discard.scissor.clear_color = FAIL 1101 WIN : dEQP-GLES3.functional.rasterizer_discard.fbo.clear_color = FAIL @@ -1068,12 +935,9 @@ 1101 WIN : dEQP-GLES3.functional.negative_api.buffer.renderbuffer_storage_multisample = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.bindtexture = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d_neg_level = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d_max_level = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage2d_invalid_size = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.teximage3d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.texsubimage3d = FAIL -1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedteximage3d_invalid_astc_target = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage3d = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.compressedtexsubimage3d_invalid_buffer_target = FAIL 1101 WIN : dEQP-GLES3.functional.negative_api.texture.texstorage2d = FAIL @@ -1101,10 +965,6 @@ 1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_0 = FAIL 1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_3 = FAIL 1101 WIN : dEQP-GLES3.functional.read_pixels.skip.choose_3_5 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.primitive_restart_fixed_index_getboolean = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.primitive_restart_fixed_index_getinteger = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.primitive_restart_fixed_index_getinteger64 = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.boolean.primitive_restart_fixed_index_getfloat = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getboolean = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getinteger = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.boolean.rasterizer_discard_getinteger64 = FAIL @@ -1142,7 +1002,6 @@ 1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getinteger = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getinteger64 = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.element_array_buffer_binding_getfloat = FAIL -1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getboolean = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getinteger = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getinteger64 = FAIL 1101 WIN : dEQP-GLES3.functional.state_query.integers.transform_feedback_binding_getfloat = FAIL @@ -1192,29 +1051,13 @@ 1101 WIN : dEQP-GLES3.functional.state_query.internal_format.rgb_samples = FAIL 1101 WIN : dEQP-GLES3.functional.polygon_offset.fixed16_render_with_units = FAIL 1101 WIN : dEQP-GLES3.functional.polygon_offset.fixed24_render_with_units = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements.points.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements.triangle_strip.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements.lines.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements.line_strip.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements.line_loop.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_arrays_instanced.line_loop.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_elements_instanced.line_loop.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.points.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.triangles.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.triangle_fan.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.triangle_strip.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.lines.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.line_strip.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.draw_range_elements.line_loop.instanced_attributes = FAIL -1101 WIN : dEQP-GLES3.functional.draw.random.50 = FAIL -1101 WIN : dEQP-GLES3.functional.draw.random.186 = FAIL -1101 WIN : dEQP-GLES3.functional.draw.random.210 = FAIL 1101 WIN : dEQP-GLES3.functional.flush_finish.flush = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.gen.transform_feedback = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.gen.vertex_array = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.attach.deleted_input.buffer_vertex_array = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.attach.deleted_name.buffer_vertex_array = FAIL 1101 WIN : dEQP-GLES3.functional.lifetime.delete_active.transform_feedback = FAIL +1101 WIN : dEQP-GLES3.functional.lifetime.delete_bound.transform_feedback = SKIP // TODO(jmadill): Find why this fails when run in a certain sequence, but not singly. 1098 WIN : dEQP-GLES3.functional.uniform_api.random.50 = FAIL diff --git a/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp b/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp index d042646064dc..cb299adb445c 100644 --- a/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp +++ b/gfx/angle/src/tests/egl_tests/EGLSurfaceTest.cpp @@ -381,6 +381,7 @@ TEST_F(EGLSurfaceTest, CreateWithEGLConfig5650Support) ASSERT_EGL_SUCCESS(); GLuint program = createProgram(); + ASSERT_NE(0u, program); drawWithProgram(program); EXPECT_GL_NO_ERROR(); glDeleteProgram(program); @@ -422,6 +423,7 @@ TEST_F(EGLSurfaceTest, CreateWithEGLConfig4444Support) ASSERT_EGL_SUCCESS(); GLuint program = createProgram(); + ASSERT_NE(0u, program); drawWithProgram(program); EXPECT_GL_NO_ERROR(); glDeleteProgram(program); @@ -463,6 +465,7 @@ TEST_F(EGLSurfaceTest, CreateWithEGLConfig5551Support) ASSERT_EGL_SUCCESS(); GLuint program = createProgram(); + ASSERT_NE(0u, program); drawWithProgram(program); EXPECT_GL_NO_ERROR(); glDeleteProgram(program); @@ -504,6 +507,7 @@ TEST_F(EGLSurfaceTest, CreateWithEGLConfig8880Support) ASSERT_EGL_SUCCESS(); GLuint program = createProgram(); + ASSERT_NE(0u, program); drawWithProgram(program); EXPECT_GL_NO_ERROR(); glDeleteProgram(program); diff --git a/gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp b/gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp index dc7f43b3ac6d..3660c194c0aa 100644 --- a/gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp +++ b/gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp @@ -43,7 +43,7 @@ class D3D11EmulatedIndexedBufferTest : public ANGLETest GLubyte indices[] = {0, 0, 3, 4, 2, 1, 1}; - for (size_t i = 0; i < _countof(indices); i++) + for (size_t i = 0; i < ArraySize(indices); i++) { mExpectedExpandedData.push_back(testData[indices[i]]); mubyteIndices.push_back(indices[i]); diff --git a/gfx/angle/src/tests/gl_tests/DifferentStencilMasksTest.cpp b/gfx/angle/src/tests/gl_tests/DifferentStencilMasksTest.cpp new file mode 100644 index 000000000000..7e1cd8a19481 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/DifferentStencilMasksTest.cpp @@ -0,0 +1,178 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// DifferentStencilMasksTest: +// Tests the equality between stencilWriteMask and stencilBackWriteMask. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ +class DifferentStencilMasksTest : public ANGLETest +{ + protected: + DifferentStencilMasksTest() : mProgram(0) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(24); + setConfigStencilBits(8); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + const std::string vertexShaderSource = SHADER_SOURCE + ( + precision highp float; + attribute vec4 position; + + void main() + { + gl_Position = position; + } + ); + + const std::string fragmentShaderSource = SHADER_SOURCE + ( + precision highp float; + + void main() + { + gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); + } + ); + + mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); + ASSERT_NE(0u, mProgram); + + glEnable(GL_STENCIL_TEST); + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDisable(GL_STENCIL_TEST); + if (mProgram != 0) + glDeleteProgram(mProgram); + + ANGLETest::TearDown(); + } + + GLuint mProgram; +}; + +// Tests that effectively same front and back masks are legal. +TEST_P(DifferentStencilMasksTest, DrawWithSameEffectiveMask) +{ + // 0x00ff and 0x01ff are effectively 0x00ff by being masked by the current stencil bits, 8. + glStencilMaskSeparate(GL_FRONT, 0x00ff); + glStencilMaskSeparate(GL_BACK, 0x01ff); + + glUseProgram(mProgram); + + drawQuad(mProgram, "position", 0.5f); + + EXPECT_GL_NO_ERROR(); +} + +// Tests that effectively different front and back masks are illegal. +TEST_P(DifferentStencilMasksTest, DrawWithDifferentMask) +{ + glStencilMaskSeparate(GL_FRONT, 0x0001); + glStencilMaskSeparate(GL_BACK, 0x0002); + + glUseProgram(mProgram); + + drawQuad(mProgram, "position", 0.5f); + + EXPECT_GL_ERROR(GL_INVALID_OPERATION); +} + +class DifferentStencilMasksWithoutStencilBufferTest : public ANGLETest +{ + protected: + DifferentStencilMasksWithoutStencilBufferTest() : mProgram(0) + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(0); + setConfigStencilBits(0); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + const std::string vertexShaderSource = SHADER_SOURCE + ( + precision highp float; + attribute vec4 position; + + void main() + { + gl_Position = position; + } + ); + + const std::string fragmentShaderSource = SHADER_SOURCE + ( + precision highp float; + + void main() + { + gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); + } + ); + + mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); + ASSERT_NE(0u, mProgram); + + glEnable(GL_STENCIL_TEST); + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDisable(GL_STENCIL_TEST); + if (mProgram != 0) + glDeleteProgram(mProgram); + + ANGLETest::TearDown(); + } + + GLuint mProgram; +}; + +// Tests that effectively different front and back masks, without stencil bits, are legal. +TEST_P(DifferentStencilMasksWithoutStencilBufferTest, DrawWithDifferentMask) +{ + glStencilMaskSeparate(GL_FRONT, 0x0001); + glStencilMaskSeparate(GL_BACK, 0x0002); + + glUseProgram(mProgram); + + drawQuad(mProgram, "position", 0.5f); + + EXPECT_GL_NO_ERROR(); +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these +// tests should be run against. +ANGLE_INSTANTIATE_TEST(DifferentStencilMasksTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +ANGLE_INSTANTIATE_TEST(DifferentStencilMasksWithoutStencilBufferTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); +} // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp b/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp index 0543c5a13f33..97b3d76bc4c7 100644 --- a/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp +++ b/gfx/angle/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp @@ -30,12 +30,7 @@ TEST_P(DiscardFramebufferEXTTest, ExtensionEnabled) if (platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) { - // EXPECT_TRUE(extensionEnabled("EXT_discard_framebuffer")); - - // EXT_discard_framebuffer is disabled in D3D11 ANGLE due to Chromium BUG:497445 - // Enabling this extension (even as a no-op) causes WebGL video failures in Chromium - // Once this bug is fixed, we can reenable the extension. - EXPECT_FALSE(extensionEnabled("EXT_discard_framebuffer")); + EXPECT_TRUE(extensionEnabled("EXT_discard_framebuffer")); } else { diff --git a/gfx/angle/src/tests/gl_tests/GLSLTest.cpp b/gfx/angle/src/tests/gl_tests/GLSLTest.cpp index c915ec4cca65..0be58e950d3b 100644 --- a/gfx/angle/src/tests/gl_tests/GLSLTest.cpp +++ b/gfx/angle/src/tests/gl_tests/GLSLTest.cpp @@ -346,6 +346,91 @@ class GLSLTest : public ANGLETest } } + void CompileGLSLWithUniformsAndSamplers(GLint vertexUniformCount, + GLint fragmentUniformCount, + GLint vertexSamplersCount, + GLint fragmentSamplersCount, + bool expectSuccess) + { + std::stringstream vertexShader; + std::stringstream fragmentShader; + + // Generate the vertex shader + vertexShader << "precision mediump float;\n"; + + for (int i = 0; i < vertexUniformCount; i++) + { + vertexShader << "uniform vec4 v" << i << ";\n"; + } + + for (int i = 0; i < vertexSamplersCount; i++) + { + vertexShader << "uniform sampler2D s" << i << ";\n"; + } + + vertexShader << "void main()\n{\n"; + + for (int i = 0; i < vertexUniformCount; i++) + { + vertexShader << " gl_Position += v" << i << ";\n"; + } + + for (int i = 0; i < vertexSamplersCount; i++) + { + vertexShader << " gl_Position += texture2D(s" << i << ", vec2(0.0, 0.0));\n"; + } + + if (vertexUniformCount == 0 && vertexSamplersCount == 0) + { + vertexShader << " gl_Position = vec4(0.0);\n"; + } + + vertexShader << "}\n"; + + // Generate the fragment shader + fragmentShader << "precision mediump float;\n"; + + for (int i = 0; i < fragmentUniformCount; i++) + { + fragmentShader << "uniform vec4 v" << i << ";\n"; + } + + for (int i = 0; i < fragmentSamplersCount; i++) + { + fragmentShader << "uniform sampler2D s" << i << ";\n"; + } + + fragmentShader << "void main()\n{\n"; + + for (int i = 0; i < fragmentUniformCount; i++) + { + fragmentShader << " gl_FragColor += v" << i << ";\n"; + } + + for (int i = 0; i < fragmentSamplersCount; i++) + { + fragmentShader << " gl_FragColor += texture2D(s" << i << ", vec2(0.0, 0.0));\n"; + } + + if (fragmentUniformCount == 0 && fragmentSamplersCount == 0) + { + fragmentShader << " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"; + } + + fragmentShader << "}\n"; + + GLuint program = CompileProgram(vertexShader.str(), fragmentShader.str()); + + if (expectSuccess) + { + EXPECT_NE(0u, program); + } + else + { + EXPECT_EQ(0u, program); + } + } + std::string mSimpleVSSource; }; @@ -743,8 +828,14 @@ TEST_P(GLSLTest, MaxVaryingVec3Array) } // Disabled because of a failure in D3D9 -TEST_P(GLSLTest, DISABLED_MaxVaryingVec3AndOneFloat) +TEST_P(GLSLTest, MaxVaryingVec3AndOneFloat) { + if (isD3D9()) + { + std::cout << "Test disabled on D3D9." << std::endl; + return; + } + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -752,8 +843,14 @@ TEST_P(GLSLTest, DISABLED_MaxVaryingVec3AndOneFloat) } // Disabled because of a failure in D3D9 -TEST_P(GLSLTest, DISABLED_MaxVaryingVec3ArrayAndOneFloatArray) +TEST_P(GLSLTest, MaxVaryingVec3ArrayAndOneFloatArray) { + if (isD3D9()) + { + std::cout << "Test disabled on D3D9." << std::endl; + return; + } + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -761,8 +858,14 @@ TEST_P(GLSLTest, DISABLED_MaxVaryingVec3ArrayAndOneFloatArray) } // Disabled because of a failure in D3D9 -TEST_P(GLSLTest, DISABLED_TwiceMaxVaryingVec2) +TEST_P(GLSLTest, TwiceMaxVaryingVec2) { + if (isD3D9()) + { + std::cout << "Test disabled on D3D9." << std::endl; + return; + } + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -770,8 +873,14 @@ TEST_P(GLSLTest, DISABLED_TwiceMaxVaryingVec2) } // Disabled because of a failure in D3D9 -TEST_P(GLSLTest, DISABLED_MaxVaryingVec2Arrays) +TEST_P(GLSLTest, MaxVaryingVec2Arrays) { + if (isD3DSM3()) + { + std::cout << "Test disabled on SM3." << std::endl; + return; + } + GLint maxVaryings = 0; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings); @@ -1132,160 +1241,78 @@ TEST_P(GLSLTest, LoopIndexingValidation) // can actually be used. TEST_P(GLSLTest, VerifyMaxVertexUniformVectors) { - const std::string &fragmentShaderSource = SHADER_SOURCE - ( - precision mediump float; - void main() - { - gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); - } - ); - int maxUniforms = 10000; glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl; - std::stringstream vshaderSource; - vshaderSource << "precision mediump float;\n"; + CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, 0, 0, true); +} - for (int i = 0; i < maxUniforms; i++) - { - vshaderSource << "uniform vec4 v" << i << ";\n"; - } +// Tests that the maximum uniforms count returned from querying GL_MAX_VERTEX_UNIFORM_VECTORS +// can actually be used along with the maximum number of texture samplers. +TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsWithSamplers) +{ + int maxUniforms = 10000; + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); + EXPECT_GL_NO_ERROR(); + std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS = " << maxUniforms << std::endl; - vshaderSource << "void main()\n{\n"; + int maxTextureImageUnits = 0; + glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); - for (int i = 0; i < maxUniforms; i++) - { - vshaderSource << " gl_Position += v" << i << ";\n"; - } - - vshaderSource << "}\n"; - - GLuint program = CompileProgram(vshaderSource.str(), fragmentShaderSource); - EXPECT_NE(0u, program); + CompileGLSLWithUniformsAndSamplers(maxUniforms, 0, maxTextureImageUnits, 0, true); } // Tests that the maximum uniforms count + 1 from querying GL_MAX_VERTEX_UNIFORM_VECTORS // fails shader compilation. TEST_P(GLSLTest, VerifyMaxVertexUniformVectorsExceeded) { - const std::string &fragmentShaderSource = SHADER_SOURCE - ( - precision mediump float; - void main() - { - gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); - } - ); - int maxUniforms = 10000; glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); - // Add 1 more to exceed the reported max count - maxUniforms += 1; - std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS + 1 = " << maxUniforms << std::endl; + std::cout << "Validating GL_MAX_VERTEX_UNIFORM_VECTORS + 1 = " << maxUniforms + 1 << std::endl; - std::stringstream vshaderSource; - vshaderSource << "precision mediump float;\n"; - - for (int i = 0; i < maxUniforms; i++) - { - vshaderSource << "uniform vec4 v" << i << ";\n"; - } - - vshaderSource << "void main()\n{\n"; - - for (int i = 0; i < maxUniforms; i++) - { - vshaderSource << " gl_Position += v" << i << ";\n"; - } - - vshaderSource << "}\n"; - - GLuint program = CompileProgram(vshaderSource.str(), fragmentShaderSource); - EXPECT_EQ(0u, program); + CompileGLSLWithUniformsAndSamplers(maxUniforms + 1, 0, 0, 0, false); } // Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS // can actually be used. -TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsFragment) +TEST_P(GLSLTest, VerifyMaxFragmentUniformVectors) { - const std::string &vertexShaderSource = SHADER_SOURCE - ( - precision mediump float; - void main() - { - gl_Position = vec4(0.0); - } - ); - int maxUniforms = 10000; glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS = " << maxUniforms << std::endl; - std::stringstream fshaderSource; - fshaderSource << "precision mediump float;\n"; + CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, 0, true); +} - for (int i = 0; i < maxUniforms; i++) - { - fshaderSource << "uniform vec4 v" << i << ";\n"; - } +// Tests that the maximum uniforms count returned from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS +// can actually be used along with the maximum number of texture samplers. +TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsWithSamplers) +{ + int maxUniforms = 10000; + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); + EXPECT_GL_NO_ERROR(); - fshaderSource << "void main()\n{\n"; + int maxTextureImageUnits = 0; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); - for (int i = 0; i < maxUniforms; i++) - { - fshaderSource << " gl_FragColor += v" << i << ";\n"; - } - - fshaderSource << "}\n"; - - GLuint program = CompileProgram(vertexShaderSource, fshaderSource.str()); - EXPECT_NE(0u, program); + CompileGLSLWithUniformsAndSamplers(0, maxUniforms, 0, maxTextureImageUnits, true); } // Tests that the maximum uniforms count + 1 from querying GL_MAX_FRAGMENT_UNIFORM_VECTORS // fails shader compilation. -TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsFragmentExceeded) +TEST_P(GLSLTest, VerifyMaxFragmentUniformVectorsExceeded) { - const std::string &vertexShaderSource = SHADER_SOURCE - ( - precision mediump float; - void main() - { - gl_Position = vec4(0.0); - } - ); - int maxUniforms = 10000; glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxUniforms); EXPECT_GL_NO_ERROR(); - // Add 1 more to exceed the reported max count - maxUniforms += 1; - std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS + 1 = " << maxUniforms << std::endl; + std::cout << "Validating GL_MAX_FRAGMENT_UNIFORM_VECTORS + 1 = " << maxUniforms + 1 + << std::endl; - std::stringstream fshaderSource; - fshaderSource << "precision mediump float;\n"; - - for (int i = 0; i < maxUniforms; i++) - { - fshaderSource << "uniform vec4 v" << i << ";\n"; - } - - fshaderSource << "void main()\n{\n"; - - for (int i = 0; i < maxUniforms; i++) - { - fshaderSource << " gl_FragColor += v" << i << ";\n"; - } - - fshaderSource << "}\n"; - - GLuint program = CompileProgram(vertexShaderSource, fshaderSource.str()); - EXPECT_EQ(0u, program); + CompileGLSLWithUniformsAndSamplers(0, maxUniforms + 1, 0, 0, false); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. diff --git a/gfx/angle/src/tests/gl_tests/ProgramBinaryTest.cpp b/gfx/angle/src/tests/gl_tests/ProgramBinaryTest.cpp index 8222b572fd7e..26f7809e5c52 100644 --- a/gfx/angle/src/tests/gl_tests/ProgramBinaryTest.cpp +++ b/gfx/angle/src/tests/gl_tests/ProgramBinaryTest.cpp @@ -28,7 +28,7 @@ class ProgramBinaryTest : public ANGLETest setConfigAlphaBits(8); } - virtual void SetUp() + void SetUp() override { ANGLETest::SetUp(); @@ -63,7 +63,7 @@ class ProgramBinaryTest : public ANGLETest ASSERT_GL_NO_ERROR(); } - virtual void TearDown() + void TearDown() override { glDeleteProgram(mProgram); glDeleteBuffers(1, &mBuffer); @@ -255,7 +255,7 @@ class ProgramBinaryTransformFeedbackTest : public ANGLETest ASSERT_GL_NO_ERROR(); } - virtual void TearDown() + void TearDown() override { glDeleteProgram(mProgram); diff --git a/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp b/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp new file mode 100644 index 000000000000..ff4564be8d81 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/ProvokingVertexTest.cpp @@ -0,0 +1,305 @@ +// +// Copyright 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ProvkingVertexTest: +// Tests on the conformance of the provoking vertex, which applies to flat +// shading and compatibility with D3D. See the section on 'flatshading' +// in the ES 3 specs. +// + +#include "test_utils/ANGLETest.h" + +using namespace angle; + +namespace +{ + +class ProvokingVertexTest : public ANGLETest +{ + protected: + ProvokingVertexTest() + : mProgram(0), + mFramebuffer(0), + mTexture(0), + mTransformFeedback(0), + mBuffer(0), + mIntAttribLocation(-1) + { + setWindowWidth(64); + setWindowHeight(64); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + setConfigDepthBits(24); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + const std::string &vertexShader = + "#version 300 es\n" + "in int intAttrib;\n" + "in vec2 position;\n" + "flat out int attrib;\n" + "void main() {\n" + " gl_Position = vec4(position, 0, 1);\n" + " attrib = intAttrib;\n" + "}"; + + const std::string &fragmentShader = + "#version 300 es\n" + "flat in int attrib;\n" + "out int fragColor;\n" + "void main() {\n" + " fragColor = attrib;\n" + "}"; + + std::vector tfVaryings; + tfVaryings.push_back("attrib"); + mProgram = CompileProgramWithTransformFeedback(vertexShader, fragmentShader, tfVaryings, + GL_SEPARATE_ATTRIBS); + ASSERT_NE(0u, mProgram); + + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32I, getWindowWidth(), getWindowHeight()); + + glGenFramebuffers(1, &mFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0); + + mIntAttribLocation = glGetAttribLocation(mProgram, "intAttrib"); + ASSERT_NE(-1, mIntAttribLocation); + glEnableVertexAttribArray(mIntAttribLocation); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + if (mProgram != 0) + { + glDeleteProgram(mProgram); + mProgram = 0; + } + + if (mFramebuffer != 0) + { + glDeleteFramebuffers(1, &mFramebuffer); + mFramebuffer = 0; + } + + if (mTexture != 0) + { + glDeleteTextures(1, &mTexture); + mTexture = 0; + } + + if (mTransformFeedback != 0) + { + glDeleteTransformFeedbacks(1, &mTransformFeedback); + mTransformFeedback = 0; + } + + if (mBuffer != 0) + { + glDeleteBuffers(1, &mBuffer); + mBuffer = 0; + } + + ANGLETest::TearDown(); + } + + GLuint mProgram; + GLuint mFramebuffer; + GLuint mTexture; + GLuint mTransformFeedback; + GLuint mBuffer; + GLint mIntAttribLocation; +}; + +// Test drawing a simple triangle with flat shading, and different valued vertices. +TEST_P(ProvokingVertexTest, FlatTriangle) +{ + GLint vertexData[] = {1, 2, 3, 1, 2, 3}; + glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData); + + drawQuad(mProgram, "position", 0.5f); + + GLint pixelValue = 0; + glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue); + + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(vertexData[2], pixelValue); +} + +// Ensure that any provoking vertex shenanigans still gives correct vertex streams. +TEST_P(ProvokingVertexTest, FlatTriWithTransformFeedback) +{ + glGenTransformFeedbacks(1, &mTransformFeedback); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback); + + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mBuffer); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 128, nullptr, GL_STREAM_DRAW); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mBuffer); + + GLint vertexData[] = {1, 2, 3, 1, 2, 3}; + glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData); + + glBeginTransformFeedback(GL_TRIANGLES); + drawQuad(mProgram, "position", 0.5f); + glEndTransformFeedback(); + + GLint pixelValue = 0; + glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue); + + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(vertexData[2], pixelValue); + + GLvoid *mapPointer = + glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(int) * 6, GL_MAP_READ_BIT); + ASSERT_NE(nullptr, mapPointer); + + int *mappedInts = static_cast(mapPointer); + for (unsigned int cnt = 0; cnt < 6; ++cnt) + { + EXPECT_EQ(vertexData[cnt], mappedInts[cnt]); + } +} + +// Test drawing a simple line with flat shading, and different valued vertices. +TEST_P(ProvokingVertexTest, FlatLine) +{ + GLfloat halfPixel = 1.0f / static_cast(getWindowWidth()); + + GLint vertexData[] = {1, 2}; + GLfloat positionData[] = {-1.0f + halfPixel, -1.0f, -1.0f + halfPixel, 1.0f}; + + glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData); + + glUseProgram(mProgram); + glDrawArrays(GL_LINES, 0, 2); + + GLint pixelValue = 0; + glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_INT, &pixelValue); + + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(vertexData[1], pixelValue); +} + +// Test drawing a simple triangle strip with flat shading, and different valued vertices. +TEST_P(ProvokingVertexTest, FlatTriStrip) +{ + GLint vertexData[] = {1, 2, 3, 4, 5, 6}; + GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, + 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f}; + + glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData); + + glUseProgram(mProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); + + std::vector pixelBuffer(getWindowWidth() * getWindowHeight(), 0); + glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT, + &pixelBuffer[0]); + + ASSERT_GL_NO_ERROR(); + + for (unsigned int triIndex = 0; triIndex < 4; ++triIndex) + { + GLfloat sumX = positionData[triIndex * 2 + 0] + positionData[triIndex * 2 + 2] + + positionData[triIndex * 2 + 4]; + GLfloat sumY = positionData[triIndex * 2 + 1] + positionData[triIndex * 2 + 3] + + positionData[triIndex * 2 + 5]; + + float centerX = sumX / 3.0f * 0.5f + 0.5f; + float centerY = sumY / 3.0f * 0.5f + 0.5f; + unsigned int pixelX = + static_cast(centerX * static_cast(getWindowWidth())); + unsigned int pixelY = + static_cast(centerY * static_cast(getWindowHeight())); + unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX; + + unsigned int provokingVertexIndex = triIndex + 2; + + EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]); + } +} + +// Test drawing an indexed triangle strip with flat shading and primitive restart. +TEST_P(ProvokingVertexTest, FlatTriStripPrimitiveRestart) +{ + // TODO(jmadill): Implement on the D3D back-end. + if (isD3D11()) + { + std::cout << "Test disabled on D3D11." << std::endl; + return; + } + + GLint indexData[] = {0, 1, 2, -1, 1, 2, 3, 4, -1, 3, 4, 5}; + GLint vertexData[] = {1, 2, 3, 4, 5, 6}; + GLfloat positionData[] = {-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, + 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f}; + + glVertexAttribIPointer(mIntAttribLocation, 1, GL_INT, 0, vertexData); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, positionData); + + glDisable(GL_CULL_FACE); + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + glUseProgram(mProgram); + glDrawElements(GL_TRIANGLE_STRIP, 12, GL_UNSIGNED_INT, indexData); + + std::vector pixelBuffer(getWindowWidth() * getWindowHeight(), 0); + glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RED_INTEGER, GL_INT, + &pixelBuffer[0]); + + ASSERT_GL_NO_ERROR(); + + // Account for primitive restart when checking the tris. + GLint triOffsets[] = {0, 4, 5, 9}; + + for (unsigned int triIndex = 0; triIndex < 4; ++triIndex) + { + GLint vertexA = indexData[triOffsets[triIndex] + 0]; + GLint vertexB = indexData[triOffsets[triIndex] + 1]; + GLint vertexC = indexData[triOffsets[triIndex] + 2]; + + GLfloat sumX = + positionData[vertexA * 2] + positionData[vertexB * 2] + positionData[vertexC * 2]; + GLfloat sumY = positionData[vertexA * 2 + 1] + positionData[vertexB * 2 + 1] + + positionData[vertexC * 2 + 1]; + + float centerX = sumX / 3.0f * 0.5f + 0.5f; + float centerY = sumY / 3.0f * 0.5f + 0.5f; + unsigned int pixelX = + static_cast(centerX * static_cast(getWindowWidth())); + unsigned int pixelY = + static_cast(centerY * static_cast(getWindowHeight())); + unsigned int pixelIndex = pixelY * getWindowWidth() + pixelX; + + unsigned int provokingVertexIndex = triIndex + 2; + + EXPECT_EQ(vertexData[provokingVertexIndex], pixelBuffer[pixelIndex]); + } +} + +ANGLE_INSTANTIATE_TEST(ProvokingVertexTest, ES3_D3D11(), ES3_OPENGL()); + +} // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp b/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp index 8450109e13b3..878897d5ab99 100644 --- a/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp +++ b/gfx/angle/src/tests/gl_tests/TransformFeedbackTest.cpp @@ -5,6 +5,7 @@ // #include "test_utils/ANGLETest.h" +#include "Vector.h" using namespace angle; @@ -15,6 +16,10 @@ class TransformFeedbackTest : public ANGLETest { protected: TransformFeedbackTest() + : mProgram(0), + mTransformFeedbackBufferSize(0), + mTransformFeedbackBuffer(0), + mTransformFeedback(0) { setWindowWidth(128); setWindowHeight(128); @@ -28,6 +33,44 @@ class TransformFeedbackTest : public ANGLETest { ANGLETest::SetUp(); + glGenBuffers(1, &mTransformFeedbackBuffer); + mTransformFeedbackBufferSize = 1 << 24; // ~16MB + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, NULL, + GL_STATIC_DRAW); + + glGenTransformFeedbacks(1, &mTransformFeedback); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + if (mProgram != 0) + { + glDeleteProgram(mProgram); + mProgram = 0; + } + + if (mTransformFeedbackBuffer != 0) + { + glDeleteBuffers(1, &mTransformFeedbackBuffer); + mTransformFeedbackBuffer = 0; + } + + if (mTransformFeedback != 0) + { + glDeleteTransformFeedbacks(1, &mTransformFeedback); + mTransformFeedback = 0; + } + + ANGLETest::TearDown(); + } + + void compileDefaultProgram(const std::vector &tfVaryings, GLenum bufferMode) + { + ASSERT_EQ(0u, mProgram); + const std::string vertexShaderSource = SHADER_SOURCE ( precision highp float; @@ -49,49 +92,24 @@ class TransformFeedbackTest : public ANGLETest } ); - mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); - if (mProgram == 0) - { - FAIL() << "shader compilation failed."; - } - - glGenBuffers(1, &mTransformFeedbackBuffer); - mTransformFeedbackBufferSize = 1 << 24; // ~16MB - glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer); - glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, NULL, GL_STATIC_DRAW); - - ASSERT_GL_NO_ERROR(); - } - - void TearDown() override - { - glDeleteProgram(mProgram); - glDeleteBuffers(1, &mTransformFeedbackBuffer); - ANGLETest::TearDown(); + mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource, + tfVaryings, bufferMode); + ASSERT_NE(0u, mProgram); } GLuint mProgram; size_t mTransformFeedbackBufferSize; GLuint mTransformFeedbackBuffer; + GLuint mTransformFeedback; }; TEST_P(TransformFeedbackTest, ZeroSizedViewport) { // Set the program's transform feedback varyings (just gl_Position) - const GLchar* transformFeedbackVaryings[] = - { - "gl_Position" - }; - glTransformFeedbackVaryings(mProgram, - static_cast(ArraySize(transformFeedbackVaryings)), - transformFeedbackVaryings, GL_INTERLEAVED_ATTRIBS); - glLinkProgram(mProgram); - - // Re-link the program - GLint linkStatus; - glGetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus); - ASSERT_NE(linkStatus, 0); + std::vector tfVaryings; + tfVaryings.push_back("gl_Position"); + compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS); glUseProgram(mProgram); @@ -133,20 +151,13 @@ TEST_P(TransformFeedbackTest, RecordAndDraw) return; } - // Set the program's transform feedback varyings (just gl_Position) - const GLchar* transformFeedbackVaryings[] = - { - "gl_Position" - }; - glTransformFeedbackVaryings(mProgram, - static_cast(ArraySize(transformFeedbackVaryings)), - transformFeedbackVaryings, GL_INTERLEAVED_ATTRIBS); - glLinkProgram(mProgram); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); - // Re-link the program - GLint linkStatus; - glGetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus); - ASSERT_NE(linkStatus, 0); + // Set the program's transform feedback varyings (just gl_Position) + std::vector tfVaryings; + tfVaryings.push_back("gl_Position"); + compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS); glUseProgram(mProgram); @@ -220,10 +231,7 @@ TEST_P(TransformFeedbackTest, BufferBinding) glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); - // Generate a new transform feedback and buffer - GLuint transformFeedbackObject = 0; - glGenTransformFeedbacks(1, &transformFeedbackObject); - + // Generate a new buffer GLuint scratchBuffer = 0; glGenBuffers(1, &scratchBuffer); @@ -243,7 +251,7 @@ TEST_P(TransformFeedbackTest, BufferBinding) EXPECT_GL_NO_ERROR(); // Check that the buffer ID for the newly bound transform feedback is zero - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbackObject); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback); glGetIntegerv(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, ¤tBufferBinding); EXPECT_EQ(0, currentBufferBinding); @@ -267,7 +275,6 @@ TEST_P(TransformFeedbackTest, BufferBinding) EXPECT_GL_NO_ERROR(); // Clean up - glDeleteTransformFeedbacks(1, &transformFeedbackObject); glDeleteBuffers(1, &scratchBuffer); } @@ -294,13 +301,11 @@ TEST_P(TransformFeedbackTest, VertexOnly) std::vector tfVaryings; tfVaryings.push_back("varyingAttrib"); - GLuint program = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource, - tfVaryings, GL_INTERLEAVED_ATTRIBS); - ASSERT_NE(0u, program); + mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource, + tfVaryings, GL_INTERLEAVED_ATTRIBS); + ASSERT_NE(0u, mProgram); - GLuint transformFeedback; - glGenTransformFeedbacks(1, &transformFeedback); - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedback); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer); std::vector attribData; @@ -309,14 +314,14 @@ TEST_P(TransformFeedbackTest, VertexOnly) attribData.push_back(static_cast(cnt)); } - GLint attribLocation = glGetAttribLocation(program, "attrib"); + GLint attribLocation = glGetAttribLocation(mProgram, "attrib"); ASSERT_NE(-1, attribLocation); glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, &attribData[0]); glEnableVertexAttribArray(attribLocation); glBeginTransformFeedback(GL_TRIANGLES); - drawQuad(program, "position", 0.5f); + drawQuad(mProgram, "position", 0.5f); glEndTransformFeedback(); ASSERT_GL_NO_ERROR(); @@ -330,13 +335,371 @@ TEST_P(TransformFeedbackTest, VertexOnly) EXPECT_EQ(attribData[cnt], mappedFloats[cnt]); } - glDeleteTransformFeedbacks(1, &transformFeedback); - glDeleteProgram(program); - EXPECT_GL_NO_ERROR(); } -// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11()); +// Test that multiple paused transform feedbacks do not generate errors or crash +TEST_P(TransformFeedbackTest, MultiplePaused) +{ + const size_t drawSize = 1024; + std::vector transformFeedbackData(drawSize); + for (size_t i = 0; i < drawSize; i++) + { + transformFeedbackData[i] = static_cast(i + 1); + } -} // namespace + // Initialize the buffers to zero + size_t bufferSize = drawSize; + std::vector bufferInitialData(bufferSize, 0); + + const size_t transformFeedbackCount = 8; + + // clang-format off + const std::string vertexShaderSource = SHADER_SOURCE + ( #version 300 es\n + in highp vec4 position; + in float transformFeedbackInput; + out float transformFeedbackOutput; + void main(void) + { + gl_Position = position; + transformFeedbackOutput = transformFeedbackInput; + } + ); + + const std::string fragmentShaderSource = SHADER_SOURCE + ( #version 300 es\n + out mediump vec4 color; + void main(void) + { + color = vec4(1.0, 1.0, 1.0, 1.0); + } + ); + // clang-format on + + std::vector tfVaryings; + tfVaryings.push_back("transformFeedbackOutput"); + + mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource, + tfVaryings, GL_INTERLEAVED_ATTRIBS); + ASSERT_NE(0u, mProgram); + glUseProgram(mProgram); + + GLint positionLocation = glGetAttribLocation(mProgram, "position"); + glDisableVertexAttribArray(positionLocation); + glVertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f); + + GLint tfInputLocation = glGetAttribLocation(mProgram, "transformFeedbackInput"); + glEnableVertexAttribArray(tfInputLocation); + glVertexAttribPointer(tfInputLocation, 1, GL_FLOAT, false, 0, &transformFeedbackData[0]); + + glDepthMask(GL_FALSE); + glEnable(GL_DEPTH_TEST); + ASSERT_GL_NO_ERROR(); + + GLuint transformFeedbacks[transformFeedbackCount]; + glGenTransformFeedbacks(transformFeedbackCount, transformFeedbacks); + + GLuint buffers[transformFeedbackCount]; + glGenBuffers(transformFeedbackCount, buffers); + + for (size_t i = 0; i < transformFeedbackCount; i++) + { + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbacks[i]); + + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffers[i]); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize * sizeof(GLfloat), + &bufferInitialData[0], GL_DYNAMIC_DRAW); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffers[i]); + ASSERT_GL_NO_ERROR(); + + glBeginTransformFeedback(GL_POINTS); + + glDrawArrays(GL_POINTS, 0, static_cast(drawSize)); + + glPauseTransformFeedback(); + + EXPECT_GL_NO_ERROR(); + } + + for (size_t i = 0; i < transformFeedbackCount; i++) + { + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbacks[i]); + glEndTransformFeedback(); + glDeleteTransformFeedbacks(1, &transformFeedbacks[i]); + + EXPECT_GL_NO_ERROR(); + } +} +// Test that running multiple simultaneous queries and transform feedbacks from multiple EGL +// contexts returns the correct results. Helps expose bugs in ANGLE's virtual contexts. +TEST_P(TransformFeedbackTest, MultiContext) +{ + if (GetParam() == ES3_D3D11()) + { + std::cout << "Test skipped because the D3D backends cannot support simultaneous transform " + "feedback or queries on multiple contexts yet." + << std::endl; + return; + } + +#if defined(ANGLE_PLATFORM_APPLE) + if ((isNVidia() || isAMD()) && GetParam() == ES3_OPENGL()) + { + std::cout << "Test skipped on NVidia and AMD OpenGL on OSX." << std::endl; + return; + } +#endif + +#if defined(ANGLE_PLATFORM_LINUX) + if (isAMD() && GetParam() == ES3_OPENGL()) + { + std::cout << "Test skipped on AMD OpenGL on Linux." << std::endl; + return; + } +#endif + + EGLint contextAttributes[] = { + EGL_CONTEXT_MAJOR_VERSION_KHR, + GetParam().majorVersion, + EGL_CONTEXT_MINOR_VERSION_KHR, + GetParam().minorVersion, + EGL_NONE, + }; + + EGLWindow *window = getEGLWindow(); + + EGLDisplay display = window->getDisplay(); + EGLConfig config = window->getConfig(); + EGLSurface surface = window->getSurface(); + + const size_t passCount = 5; + struct ContextInfo + { + EGLContext context; + GLuint program; + GLuint query; + GLuint buffer; + size_t primitiveCounts[passCount]; + }; + ContextInfo contexts[32]; + + const size_t maxDrawSize = 1024; + + std::vector transformFeedbackData(maxDrawSize); + for (size_t i = 0; i < maxDrawSize; i++) + { + transformFeedbackData[i] = static_cast(i + 1); + } + + // Initialize the buffers to zero + size_t bufferSize = maxDrawSize * passCount; + std::vector bufferInitialData(bufferSize, 0); + + for (auto &context : contexts) + { + context.context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); + ASSERT_NE(context.context, EGL_NO_CONTEXT); + + eglMakeCurrent(display, surface, surface, context.context); + + // clang-format off + const std::string vertexShaderSource = SHADER_SOURCE + ( #version 300 es\n + in highp vec4 position; + in float transformFeedbackInput; + out float transformFeedbackOutput; + void main(void) + { + gl_Position = position; + transformFeedbackOutput = transformFeedbackInput; + } + ); + + const std::string fragmentShaderSource = SHADER_SOURCE + ( #version 300 es\n + out mediump vec4 color; + void main(void) + { + color = vec4(1.0, 1.0, 1.0, 1.0); + } + ); + // clang-format on + + std::vector tfVaryings; + tfVaryings.push_back("transformFeedbackOutput"); + + context.program = CompileProgramWithTransformFeedback( + vertexShaderSource, fragmentShaderSource, tfVaryings, GL_INTERLEAVED_ATTRIBS); + ASSERT_NE(context.program, 0u); + glUseProgram(context.program); + + GLint positionLocation = glGetAttribLocation(context.program, "position"); + glDisableVertexAttribArray(positionLocation); + glVertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f); + + GLint tfInputLocation = glGetAttribLocation(context.program, "transformFeedbackInput"); + glEnableVertexAttribArray(tfInputLocation); + glVertexAttribPointer(tfInputLocation, 1, GL_FLOAT, false, 0, &transformFeedbackData[0]); + + glDepthMask(GL_FALSE); + glEnable(GL_DEPTH_TEST); + glGenQueriesEXT(1, &context.query); + glBeginQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, context.query); + + ASSERT_GL_NO_ERROR(); + + glGenBuffers(1, &context.buffer); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, context.buffer); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize * sizeof(GLfloat), + &bufferInitialData[0], GL_DYNAMIC_DRAW); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, context.buffer); + + ASSERT_GL_NO_ERROR(); + + // For each pass, draw between 0 and maxDrawSize primitives + for (auto &primCount : context.primitiveCounts) + { + primCount = rand() % maxDrawSize; + } + + glBeginTransformFeedback(GL_POINTS); + } + + for (size_t pass = 0; pass < passCount; pass++) + { + for (const auto &context : contexts) + { + eglMakeCurrent(display, surface, surface, context.context); + + glDrawArrays(GL_POINTS, 0, static_cast(context.primitiveCounts[pass])); + } + } + + for (const auto &context : contexts) + { + eglMakeCurrent(display, surface, surface, context.context); + + glEndTransformFeedback(); + + glEndQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + + GLuint result = 0; + glGetQueryObjectuivEXT(context.query, GL_QUERY_RESULT_EXT, &result); + + EXPECT_GL_NO_ERROR(); + + size_t totalPrimCount = 0; + for (const auto &primCount : context.primitiveCounts) + { + totalPrimCount += primCount; + } + EXPECT_EQ(static_cast(totalPrimCount), result); + + const float *bufferData = reinterpret_cast(glMapBufferRange( + GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferSize * sizeof(GLfloat), GL_MAP_READ_BIT)); + + size_t curBufferIndex = 0; + for (const auto &primCount : context.primitiveCounts) + { + for (size_t prim = 0; prim < primCount; prim++) + { + EXPECT_EQ(bufferData[curBufferIndex], prim + 1); + curBufferIndex++; + } + } + + while (curBufferIndex < bufferSize) + { + EXPECT_EQ(bufferData[curBufferIndex], 0.0f); + curBufferIndex++; + } + + glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); + } + + eglMakeCurrent(display, surface, surface, window->getContext()); + + for (auto &context : contexts) + { + eglDestroyContext(display, context.context); + context.context = EGL_NO_CONTEXT; + } +} + +// Test that when two vec2s are packed into the same register, we can still capture both of them. +TEST_P(TransformFeedbackTest, PackingBug) +{ + // TODO(jmadill): With points and rasterizer discard? + const std::string &vertexShaderSource = + "#version 300 es\n" + "in vec2 inAttrib1;\n" + "in vec2 inAttrib2;\n" + "out vec2 outAttrib1;\n" + "out vec2 outAttrib2;\n" + "in vec2 position;\n" + "void main() {" + " outAttrib1 = inAttrib1;\n" + " outAttrib2 = inAttrib2;\n" + " gl_Position = vec4(position, 0, 1);\n" + "}"; + + const std::string &fragmentShaderSource = + "#version 300 es\n" + "precision mediump float;\n" + "out vec4 color;\n" + "void main() {\n" + " color = vec4(0);\n" + "}"; + + std::vector tfVaryings; + tfVaryings.push_back("outAttrib1"); + tfVaryings.push_back("outAttrib2"); + + mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource, + tfVaryings, GL_INTERLEAVED_ATTRIBS); + ASSERT_NE(0u, mProgram); + + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(Vector2) * 2 * 6, nullptr, GL_STREAM_DRAW); + + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer); + + GLint attrib1Loc = glGetAttribLocation(mProgram, "inAttrib1"); + GLint attrib2Loc = glGetAttribLocation(mProgram, "inAttrib2"); + + Vector2 attrib1Data[] = {Vector2(1.0, 2.0), Vector2(3.0, 4.0), Vector2(5.0, 6.0)}; + Vector2 attrib2Data[] = {Vector2(11.0, 12.0), Vector2(13.0, 14.0), Vector2(15.0, 16.0)}; + + glEnableVertexAttribArray(attrib1Loc); + glEnableVertexAttribArray(attrib2Loc); + + glVertexAttribPointer(attrib1Loc, 2, GL_FLOAT, GL_FALSE, 0, attrib1Data); + glVertexAttribPointer(attrib2Loc, 2, GL_FLOAT, GL_FALSE, 0, attrib2Data); + + glBeginTransformFeedback(GL_TRIANGLES); + drawQuad(mProgram, "position", 0.5f); + glEndTransformFeedback(); + ASSERT_GL_NO_ERROR(); + + const GLvoid *mapPointer = + glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(Vector2) * 2 * 6, GL_MAP_READ_BIT); + ASSERT_NE(nullptr, mapPointer); + + const Vector2 *vecPointer = static_cast(mapPointer); + for (unsigned int vectorIndex = 0; vectorIndex < 3; ++vectorIndex) + { + unsigned int stream1Index = vectorIndex * 2; + unsigned int stream2Index = vectorIndex * 2 + 1; + EXPECT_EQ(attrib1Data[vectorIndex], vecPointer[stream1Index]); + EXPECT_EQ(attrib2Data[vectorIndex], vecPointer[stream2Index]); + } + + ASSERT_GL_NO_ERROR(); +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. +ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11(), ES3_OPENGL()); + +} // anonymous namespace diff --git a/gfx/angle/src/tests/gl_tests/UniformTest.cpp b/gfx/angle/src/tests/gl_tests/UniformTest.cpp index 6d1617371c53..67467f09875e 100644 --- a/gfx/angle/src/tests/gl_tests/UniformTest.cpp +++ b/gfx/angle/src/tests/gl_tests/UniformTest.cpp @@ -454,6 +454,15 @@ TEST_P(UniformTestES3, TranposedMatrixArrayUniformStateQuery) // Check that sampler uniforms only show up one time in the list TEST_P(UniformTest, SamplerUniformsAppearOnce) { + int maxVertexTextureImageUnits = 0; + glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits); + + if (maxVertexTextureImageUnits == 0) + { + std::cout << "Renderer doesn't support vertex texture fetch, skipping test" << std::endl; + return; + } + const std::string &vertShader = "attribute vec2 position;\n" "uniform sampler2D tex2D;\n" @@ -492,7 +501,7 @@ TEST_P(UniformTest, SamplerUniformsAppearOnce) } // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. -ANGLE_INSTANTIATE_TEST(UniformTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); +ANGLE_INSTANTIATE_TEST(UniformTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3(), ES2_OPENGL()); ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL()); } // namespace diff --git a/gfx/angle/src/tests/perf_tests/BufferSubData.cpp b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp index c141eda8453b..d153b0c9361d 100644 --- a/gfx/angle/src/tests/perf_tests/BufferSubData.cpp +++ b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp @@ -45,7 +45,7 @@ struct BufferSubDataParams final : public RenderTestParams unsigned int iterations; }; -inline std::ostream &operator<<(std::ostream &os, const BufferSubDataParams ¶ms) +std::ostream &operator<<(std::ostream &os, const BufferSubDataParams ¶ms) { os << params.suffix().substr(1); return os; diff --git a/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp index 8e4d95415b49..d0fb539e8cf8 100644 --- a/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp +++ b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp @@ -55,7 +55,7 @@ struct DrawCallPerfParams final : public RenderTestParams int numTris; }; -inline std::ostream &operator<<(std::ostream &os, const DrawCallPerfParams ¶ms) +std::ostream &operator<<(std::ostream &os, const DrawCallPerfParams ¶ms) { os << params.suffix().substr(1); return os; diff --git a/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp index 31f94dbf8174..40ca08568403 100644 --- a/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp +++ b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp @@ -159,7 +159,8 @@ void IndexDataManagerPerfTest::step(float dt, double totalTime) { mIndexBuffer.getIndexRange(GL_UNSIGNED_SHORT, 0, mIndexCount, false, &translatedIndexData.indexRange); - mIndexDataManager.prepareIndexData(GL_UNSIGNED_SHORT, mIndexCount, &mIndexBuffer, nullptr, &translatedIndexData, &sourceIndexData); + mIndexDataManager.prepareIndexData(GL_UNSIGNED_SHORT, mIndexCount, &mIndexBuffer, nullptr, + &translatedIndexData, &sourceIndexData, false); } if (mTimer->getElapsedTime() >= 5.0) diff --git a/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp new file mode 100644 index 000000000000..d0f09df9d0ef --- /dev/null +++ b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp @@ -0,0 +1,236 @@ +// +// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// InterleavedAttributeData: +// Performance test for draws using interleaved attribute data in vertex buffers. +// + +#include + +#include "ANGLEPerfTest.h" +#include "shader_utils.h" + +using namespace angle; + +namespace +{ + +struct InterleavedAttributeDataParams final : public RenderTestParams +{ + InterleavedAttributeDataParams() + { + // Common default values + majorVersion = 2; + minorVersion = 0; + windowWidth = 512; + windowHeight = 512; + numSprites = 3000; + } + + // static parameters + unsigned int numSprites; +}; + +std::ostream &operator<<(std::ostream &os, const InterleavedAttributeDataParams ¶ms) +{ + os << params.suffix().substr(1); + + if (params.eglParameters.majorVersion != EGL_DONT_CARE) + { + os << "_" << params.eglParameters.majorVersion << "_" << params.eglParameters.minorVersion; + } + + return os; +} + +class InterleavedAttributeDataBenchmark + : public ANGLERenderTest, + public ::testing::WithParamInterface +{ + public: + InterleavedAttributeDataBenchmark(); + + void initializeBenchmark() override; + void destroyBenchmark() override; + void beginDrawBenchmark() override; + void drawBenchmark() override; + + private: + GLuint mPointSpriteProgram; + GLuint mPositionColorBuffer[2]; + + // The buffers contain two floats and 3 unsigned bytes per point sprite + const size_t mBytesPerSprite = 2 * sizeof(float) + 3; +}; + +InterleavedAttributeDataBenchmark::InterleavedAttributeDataBenchmark() + : ANGLERenderTest("InterleavedAttributeData", GetParam()), mPointSpriteProgram(0) +{ +} + +void InterleavedAttributeDataBenchmark::initializeBenchmark() +{ + const auto ¶ms = GetParam(); + + // Compile point sprite shaders + const std::string vs = + "attribute vec4 aPosition;" + "attribute vec4 aColor;" + "varying vec4 vColor;" + "void main()" + "{" + " gl_PointSize = 25.0;" + " gl_Position = aPosition;" + " vColor = aColor;" + "}"; + + const std::string fs = + "precision mediump float;" + "varying vec4 vColor;" + "void main()" + "{" + " gl_FragColor = vColor;" + "}"; + + mPointSpriteProgram = CompileProgram(vs, fs); + ASSERT_TRUE(mPointSpriteProgram != 0); + + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + + for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++) + { + // Set up initial data for pointsprite positions and colors + std::vector positionColorData(mBytesPerSprite * params.numSprites); + for (unsigned int j = 0; j < params.numSprites; j++) + { + float pointSpriteX = + (static_cast(rand() % getWindow()->getWidth()) / getWindow()->getWidth()) * + 2.0f - 1.0f; + float pointSpriteY = + (static_cast(rand() % getWindow()->getHeight()) / getWindow()->getHeight()) * + 2.0f - 1.0f; + GLubyte pointSpriteRed = static_cast(rand() % 255); + GLubyte pointSpriteGreen = static_cast(rand() % 255); + GLubyte pointSpriteBlue = static_cast(rand() % 255); + + // Add position data for the pointsprite + *reinterpret_cast( + &(positionColorData[j * mBytesPerSprite + 0 * sizeof(float) + 0])) = + pointSpriteX; // X + *reinterpret_cast( + &(positionColorData[j * mBytesPerSprite + 1 * sizeof(float) + 0])) = + pointSpriteY; // Y + + // Add color data for the pointsprite + positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 0] = pointSpriteRed; // R + positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 1] = pointSpriteGreen; // G + positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 2] = pointSpriteBlue; // B + } + + // Generate the GL buffer with the position/color data + glGenBuffers(1, &mPositionColorBuffer[i]); + glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]); + glBufferData(GL_ARRAY_BUFFER, params.numSprites * mBytesPerSprite, &(positionColorData[0]), + GL_STATIC_DRAW); + } + + ASSERT_GL_NO_ERROR(); +} + +void InterleavedAttributeDataBenchmark::destroyBenchmark() +{ + glDeleteProgram(mPointSpriteProgram); + + for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++) + { + glDeleteBuffers(1, &mPositionColorBuffer[i]); + } +} + +void InterleavedAttributeDataBenchmark::beginDrawBenchmark() +{ + // Clear the color buffer + glClear(GL_COLOR_BUFFER_BIT); +} + +void InterleavedAttributeDataBenchmark::drawBenchmark() +{ + for (size_t k = 0; k < 4; k++) + { + for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++) + { + // Firstly get the attribute locations for the program + glUseProgram(mPointSpriteProgram); + GLint positionLocation = glGetAttribLocation(mPointSpriteProgram, "aPosition"); + ASSERT_NE(positionLocation, -1); + GLint colorLocation = glGetAttribLocation(mPointSpriteProgram, "aColor"); + ASSERT_NE(colorLocation, -1); + + // Bind the position data from one buffer + glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, + static_cast(mBytesPerSprite), 0); + + // But bind the color data from the other buffer. + glBindBuffer(GL_ARRAY_BUFFER, + mPositionColorBuffer[(i + 1) % ArraySize(mPositionColorBuffer)]); + glEnableVertexAttribArray(colorLocation); + glVertexAttribPointer(colorLocation, 3, GL_UNSIGNED_BYTE, GL_TRUE, + static_cast(mBytesPerSprite), + reinterpret_cast(2 * sizeof(float))); + + // Then draw the colored pointsprites + glDrawArrays(GL_POINTS, 0, GetParam().numSprites); + glFlush(); + + glDisableVertexAttribArray(positionLocation); + glDisableVertexAttribArray(colorLocation); + } + } + + ASSERT_GL_NO_ERROR(); +} + +TEST_P(InterleavedAttributeDataBenchmark, Run) +{ + run(); +} + +InterleavedAttributeDataParams D3D11Params() +{ + InterleavedAttributeDataParams params; + params.eglParameters = egl_platform::D3D11(); + return params; +} + +InterleavedAttributeDataParams D3D11_9_3Params() +{ + InterleavedAttributeDataParams params; + params.eglParameters = egl_platform::D3D11_FL9_3(); + return params; +} + +InterleavedAttributeDataParams D3D9Params() +{ + InterleavedAttributeDataParams params; + params.eglParameters = egl_platform::D3D9(); + return params; +} + +InterleavedAttributeDataParams OpenGLParams() +{ + InterleavedAttributeDataParams params; + params.eglParameters = egl_platform::OPENGL(); + return params; +} + +ANGLE_INSTANTIATE_TEST(InterleavedAttributeDataBenchmark, + D3D11Params(), + D3D11_9_3Params(), + D3D9Params(), + OpenGLParams()); + +} // anonymous namespace diff --git a/gfx/angle/src/tests/perf_tests/PointSprites.cpp b/gfx/angle/src/tests/perf_tests/PointSprites.cpp index 1e85f139f7c0..4202b46ee8a4 100644 --- a/gfx/angle/src/tests/perf_tests/PointSprites.cpp +++ b/gfx/angle/src/tests/perf_tests/PointSprites.cpp @@ -45,7 +45,7 @@ struct PointSpritesParams final : public RenderTestParams unsigned int iterations; }; -inline std::ostream &operator<<(std::ostream &os, const PointSpritesParams ¶ms) +std::ostream &operator<<(std::ostream &os, const PointSpritesParams ¶ms) { os << params.suffix().substr(1); return os; diff --git a/gfx/angle/src/tests/perf_tests/TexSubImage.cpp b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp index 9f43f0a4abec..ed42bc0cd517 100644 --- a/gfx/angle/src/tests/perf_tests/TexSubImage.cpp +++ b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp @@ -44,7 +44,7 @@ struct TexSubImageParams final : public RenderTestParams unsigned int iterations; }; -inline std::ostream &operator<<(std::ostream &os, const TexSubImageParams ¶ms) +std::ostream &operator<<(std::ostream &os, const TexSubImageParams ¶ms) { os << params.suffix().substr(1); return os; diff --git a/gfx/angle/src/tests/test_utils/ANGLETest.cpp b/gfx/angle/src/tests/test_utils/ANGLETest.cpp index 4df5349504d0..a421bfc46eac 100644 --- a/gfx/angle/src/tests/test_utils/ANGLETest.cpp +++ b/gfx/angle/src/tests/test_utils/ANGLETest.cpp @@ -122,10 +122,17 @@ GLuint ANGLETest::compileShader(GLenum type, const std::string &source) GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); - std::vector infoLog(infoLogLength); - glGetShaderInfoLog(shader, static_cast(infoLog.size()), NULL, &infoLog[0]); + if (infoLogLength == 0) + { + std::cerr << "shader compilation failed with empty log." << std::endl; + } + else + { + std::vector infoLog(infoLogLength); + glGetShaderInfoLog(shader, static_cast(infoLog.size()), NULL, &infoLog[0]); - std::cerr << "shader compilation failed: " << &infoLog[0]; + std::cerr << "shader compilation failed: " << &infoLog[0]; + } glDeleteShader(shader); shader = 0; @@ -285,6 +292,30 @@ bool ANGLETest::isNVidia() const return (rendererString.find("NVIDIA") != std::string::npos); } +bool ANGLETest::isD3D11() const +{ + std::string rendererString(reinterpret_cast(glGetString(GL_RENDERER))); + return (rendererString.find("Direct3D11 vs_5_0") != std::string::npos); +} + +bool ANGLETest::isD3D11_FL93() const +{ + std::string rendererString(reinterpret_cast(glGetString(GL_RENDERER))); + return (rendererString.find("Direct3D11 vs_4_0_") != std::string::npos); +} + +bool ANGLETest::isD3D9() const +{ + std::string rendererString(reinterpret_cast(glGetString(GL_RENDERER))); + return (rendererString.find("Direct3D9") != std::string::npos); +} + +bool ANGLETest::isD3DSM3() const +{ + std::string rendererString(reinterpret_cast(glGetString(GL_RENDERER))); + return isD3D9() || isD3D11_FL93(); +} + EGLint ANGLETest::getPlatformRenderer() const { assert(mEGLWindow); diff --git a/gfx/angle/src/tests/test_utils/ANGLETest.h b/gfx/angle/src/tests/test_utils/ANGLETest.h index 856aa670bfe7..d653615c2934 100644 --- a/gfx/angle/src/tests/test_utils/ANGLETest.h +++ b/gfx/angle/src/tests/test_utils/ANGLETest.h @@ -98,6 +98,13 @@ class ANGLETest : public ::testing::TestWithParam bool isIntel() const; bool isAMD() const; bool isNVidia() const; + // Note: FL9_3 is explicitly *not* considered D3D11. + bool isD3D11() const; + bool isD3D11_FL93() const; + // Is a D3D9-class renderer. + bool isD3D9() const; + // Is D3D9 or SM9_3 renderer. + bool isD3DSM3() const; EGLint getPlatformRenderer() const; private: diff --git a/gfx/angle/src/tests/test_utils/compiler_test.cpp b/gfx/angle/src/tests/test_utils/compiler_test.cpp index 5eb740a1de8e..7a693eb4f3c7 100644 --- a/gfx/angle/src/tests/test_utils/compiler_test.cpp +++ b/gfx/angle/src/tests/test_utils/compiler_test.cpp @@ -17,11 +17,6 @@ bool compileTestShader(sh::GLenum type, std::string *translatedCode, std::string *infoLog) { - if (spec == SH_GLES3_SPEC || spec == SH_WEBGL2_SPEC) - { - resources->FragmentPrecisionHigh = 1; - } - TCompiler *translator = ConstructCompiler(type, spec, output); if (!translator->Init(*resources)) { From 3ff4f7b85eb39ae3e70b5b8b3e9121691c7c099b Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 1 Dec 2015 12:45:17 -0800 Subject: [PATCH 023/102] Backed out changeset b336ed16226d (bug 1072501) for windows t-e10s failures --HG-- extra : commitid : IuXPBtfh1Nc --- gfx/layers/TextureDIB.cpp | 17 +++++------------ gfx/layers/client/TextureClient.cpp | 1 + 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 48e68cd72185..7dcaf3c21439 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -230,9 +230,10 @@ ShmemDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, } uint8_t* data = (uint8_t*)::MapViewOfFile(fileMapping, FILE_MAP_WRITE | FILE_MAP_READ, - 0, 0, mapSize); + 0, 0, aSize.width * aSize.height + * BytesPerPixel(aFormat)); - memset(data, 0x80, mapSize); + memset(data, 0x80, aSize.width * aSize.height * BytesPerPixel(aFormat)); ::UnmapViewOfFile(fileMapping); @@ -386,14 +387,6 @@ TextureHostFileMapping::~TextureHostFileMapping() ::CloseHandle(mFileMapping); } -UserDataKey kFileMappingKey; - -static void UnmapFileData(void* aData) -{ - MOZ_ASSERT(aData); - ::UnmapViewOfFile(aData); -} - void TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) { @@ -412,14 +405,14 @@ TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) if (data) { RefPtr surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat); - surf->AddUserData(&kFileMappingKey, data, UnmapFileData); - if (!mTextureSource->Update(surf, const_cast(aRegion))) { mTextureSource = nullptr; } } else { mTextureSource = nullptr; } + + ::UnmapViewOfFile(data); } } diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index a5a60d61f927..0dfc8afefd06 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -728,6 +728,7 @@ TextureClient::CreateForDrawing(CompositableForwarder* aAllocator, } if (!data && aFormat == SurfaceFormat::B8G8R8X8 && + aAllocator->IsSameProcess() && moz2DBackend == gfx::BackendType::CAIRO && NS_IsMainThread()) { data = DIBTextureData::Create(aSize, aFormat, aAllocator); From 27d40f6deea04fdf06e8018d12601ec2aed0849d Mon Sep 17 00:00:00 2001 From: Anup Kumar Date: Tue, 1 Dec 2015 12:50:47 -0800 Subject: [PATCH 024/102] Bug 1197307 - remove PR_snprintf calls in layout/ r=froydnj r=dholbert --- layout/generic/nsBlockFrame.cpp | 16 ++++++++-------- layout/generic/nsFrame.cpp | 10 +++++----- layout/generic/nsLineBox.cpp | 20 ++++++++++---------- layout/mathml/nsMathMLChar.cpp | 4 ++-- layout/mathml/nsMathMLmactionFrame.cpp | 5 +---- layout/style/CounterStyleManager.cpp | 6 +----- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index d1145138acb9..4ffbef11b6c6 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -31,7 +31,7 @@ #include "nsGkAtoms.h" #include "nsGenericHTMLElement.h" #include "nsAttrValueInlines.h" -#include "prprf.h" +#include "mozilla/Snprintf.h" #include "nsFloatManager.h" #include "prenv.h" #include "plstr.h" @@ -1427,9 +1427,9 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, ListTag(stdout); char buf[400]; - PR_snprintf(buf, sizeof(buf), - ": %lld elapsed (%lld per line) (%d lines; %d new lines)", - delta, perLineDelta, numLines, ectc - ctc); + snprintf_literal(buf, + ": %lld elapsed (%lld per line) (%d lines; %d new lines)", + delta, perLineDelta, numLines, ectc - ctc); printf("%s\n", buf); } #endif @@ -6578,10 +6578,10 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, ListTag(stdout); char buf[400]; - PR_snprintf(buf, sizeof(buf), - ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d", - delta, deltaPerLine, - numLines, drawnLines, numLines - drawnLines); + snprintf_literal(buf, + ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d", + delta, deltaPerLine, + numLines, drawnLines, numLines - drawnLines); printf("%s\n", buf); } #endif diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 6506c0f0378b..d9f1457e3e4c 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -36,7 +36,7 @@ #include "nsStyleConsts.h" #include "nsIPresShell.h" #include "mozilla/Logging.h" -#include "prprf.h" +#include "mozilla/Snprintf.h" #include "nsFrameManager.h" #include "nsLayoutUtils.h" #include "RestyleManager.h" @@ -5923,7 +5923,7 @@ nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const aResult.Append(')'); } char buf[40]; - PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this)); + snprintf_literal(buf, "(%d)", ContentIndexInContainer(this)); AppendASCIItoUTF16(buf, aResult); return NS_OK; } @@ -9154,11 +9154,11 @@ GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize, char* aResult) { if (aContent) { - PR_snprintf(aResult, aResultSize, "%s@%p", - nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame); + snprintf(aResult, aResultSize, "%s@%p", + nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame); } else { - PR_snprintf(aResult, aResultSize, "@%p", aFrame); + snprintf(aResult, aResultSize, "@%p", aFrame); } } diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp index b004e1cfbdc3..cf67c604b124 100644 --- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -17,7 +17,7 @@ #include "nsIFrameInlines.h" #include "nsPresArena.h" #include "nsPrintfCString.h" -#include "prprf.h" +#include "mozilla/Snprintf.h" #ifdef DEBUG static int32_t ctorCount; @@ -213,15 +213,15 @@ BreakTypeToString(uint8_t aBreakType) char* nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const { - PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]", - IsBlock() ? "block" : "inline", - IsDirty() ? "dirty" : "clean", - IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", - IsImpactedByFloat() ? "impacted" : "not impacted", - IsLineWrapped() ? "wrapped" : "not wrapped", - BreakTypeToString(GetBreakTypeBefore()), - BreakTypeToString(GetBreakTypeAfter()), - mAllFlags); + snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]", + IsBlock() ? "block" : "inline", + IsDirty() ? "dirty" : "clean", + IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", + IsImpactedByFloat() ? "impacted" : "not impacted", + IsLineWrapped() ? "wrapped" : "not wrapped", + BreakTypeToString(GetBreakTypeBefore()), + BreakTypeToString(GetBreakTypeAfter()), + mAllFlags); return aBuf; } diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index f8b2f4e53b89..4923ab29c341 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -30,7 +30,7 @@ #include "mozilla/LookAndFeel.h" #include "nsCSSRendering.h" -#include "prprf.h" // For PR_snprintf() +#include "mozilla/Snprintf.h" #include "nsDisplayList.h" @@ -314,7 +314,7 @@ nsPropertiesTable::ElementAt(gfxContext* /* aThebesContext */, if (mCharCache != aChar) { // The key in the property file is interpreted as ASCII and kept // as such ... - char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", aChar); + char key[10]; snprintf_literal(key, "\\u%04X", aChar); nsAutoString value; nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), value); diff --git a/layout/mathml/nsMathMLmactionFrame.cpp b/layout/mathml/nsMathMLmactionFrame.cpp index 593f020fbf8b..1e027ceab8a0 100644 --- a/layout/mathml/nsMathMLmactionFrame.cpp +++ b/layout/mathml/nsMathMLmactionFrame.cpp @@ -7,7 +7,6 @@ #include "nsCOMPtr.h" #include "nsPresContext.h" #include "nsNameSpaceManager.h" -#include "prprf.h" // For PR_snprintf() #include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" #include "nsIWebBrowserChrome.h" @@ -326,9 +325,7 @@ nsMathMLmactionFrame::MouseClick() if (mChildCount > 1) { int32_t selection = (mSelection == mChildCount)? 1 : mSelection + 1; nsAutoString value; - char cbuf[10]; - PR_snprintf(cbuf, sizeof(cbuf), "%d", selection); - value.AssignASCII(cbuf); + value.AppendInt(selection); bool notify = false; // don't yet notify the document mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value, notify); diff --git a/layout/style/CounterStyleManager.cpp b/layout/style/CounterStyleManager.cpp index 7c8e27f6b3e0..8846f80daaa0 100644 --- a/layout/style/CounterStyleManager.cpp +++ b/layout/style/CounterStyleManager.cpp @@ -18,7 +18,6 @@ #include "nsTArray.h" #include "nsTHashtable.h" #include "nsUnicodeProperties.h" -#include "prprf.h" namespace mozilla { @@ -210,10 +209,7 @@ GetAdditiveCounterText(CounterValue aOrdinal, static bool DecimalToText(CounterValue aOrdinal, nsSubstring& aResult) { - // 3 for additional digit, negative sign, and null - char cbuf[std::numeric_limits::digits10 + 3]; - PR_snprintf(cbuf, sizeof(cbuf), "%ld", aOrdinal); - aResult.AssignASCII(cbuf); + aResult.AppendInt(aOrdinal); return true; } From cbc69d484699bec3b3c508051c2148838a78012d Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Dec 2015 15:54:17 -0500 Subject: [PATCH 025/102] Bug 1229393 - Ensure that all the touch points from the original touch go to APZCCallbackHelper::SendSetTargetAPZCNotification. r=botond --- widget/nsBaseWidget.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 31b40108403c..033269fce4d2 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -995,7 +995,11 @@ nsBaseWidget::ProcessUntransformedAPZEvent(WidgetInputEvent* aEvent, GetDefaultScale()); } + // Make a copy of the original event for the APZCCallbackHelper helpers that + // we call later, because the event passed to DispatchEvent can get mutated in + // ways that we don't want (i.e. touch points can get stripped out). nsEventStatus status; + UniquePtr original(aEvent->Duplicate()); DispatchEvent(aEvent, status); if (mAPZC && !context.WasRoutedToChildProcess()) { @@ -1006,18 +1010,19 @@ nsBaseWidget::ProcessUntransformedAPZEvent(WidgetInputEvent* aEvent, if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) { if (touchEvent->mMessage == eTouchStart) { if (gfxPrefs::TouchActionEnabled()) { - APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(this, *touchEvent, - aInputBlockId, mSetAllowedTouchBehaviorCallback); + APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(this, + *(original->AsTouchEvent()), aInputBlockId, + mSetAllowedTouchBehaviorCallback); } - APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(), *aEvent, - aGuid, aInputBlockId); + APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(), + *(original->AsTouchEvent()), aGuid, aInputBlockId); } mAPZEventState->ProcessTouchEvent(*touchEvent, aGuid, aInputBlockId, aApzResponse, status); } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) { if (wheelEvent->mFlags.mHandledByAPZ) { - APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(), *aEvent, - aGuid, aInputBlockId); + APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(), + *(original->AsWheelEvent()), aGuid, aInputBlockId); if (wheelEvent->mCanTriggerSwipe) { ReportSwipeStarted(aInputBlockId, wheelEvent->TriggersSwipe()); } From a658f26611765137e84c0972fa73ba0606c14110 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 1 Dec 2015 13:32:01 -0800 Subject: [PATCH 026/102] Bug 1229117 - Make test_bug741266.html wait for the window to be opened. r=bz DONTBUILD Change this test to open "/" instead of about:blank so we get an onload event. Then, wait for the window to be loaded before examining it. --- dom/html/test/test_bug741266.html | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/dom/html/test/test_bug741266.html b/dom/html/test/test_bug741266.html index 1da30079d8ea..72db2807978e 100644 --- a/dom/html/test/test_bug741266.html +++ b/dom/html/test/test_bug741266.html @@ -13,22 +13,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741266 Mozilla Bug 741266

 
 
From 7ae8605a513ed379fd06713f75da32159a25f24c Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Wed, 2 Dec 2015 00:09:05 +1100 Subject: [PATCH 027/102] Bug 1229339: Partial revert of commit c15c9f37f. r=cpearce Replacement of nsTArray by MediaByteRangeSet in the MoofParser was incorrect. --- media/libstagefright/binding/MoofParser.cpp | 6 +++--- .../libstagefright/binding/include/mp4_demuxer/MoofParser.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 310539048e44..edc4eff3b29c 100644 --- a/media/libstagefright/binding/MoofParser.cpp +++ b/media/libstagefright/binding/MoofParser.cpp @@ -59,7 +59,7 @@ MoofParser::RebuildFragmentedIndex(BoxContext& aContext) } mMoofs.AppendElement(moof); - mMediaRanges += moof.mRange; + mMediaRanges.AppendElement(moof.mRange); foundValidMoof = true; } else if (box.IsType("mdat") && !Moofs().IsEmpty()) { // Check if we have all our data from last moof. @@ -67,8 +67,8 @@ MoofParser::RebuildFragmentedIndex(BoxContext& aContext) media::Interval datarange(moof.mMdatRange.mStart, moof.mMdatRange.mEnd, 0); media::Interval mdat(box.Range().mStart, box.Range().mEnd, 0); if (datarange.Intersects(mdat)) { - mMediaRanges.LastInterval() = - mMediaRanges.LastInterval().Span(box.Range()); + mMediaRanges.LastElement() = + mMediaRanges.LastElement().Span(box.Range()); } } mOffset = box.NextOffset(); diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h index af5b8ed80289..42cae264392e 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -245,7 +245,7 @@ private: void ScanForMetadata(mozilla::MediaByteRange& aFtyp, mozilla::MediaByteRange& aMoov); nsTArray mMoofs; - MediaByteRangeSet mMediaRanges; + nsTArray mMediaRanges; bool mIsAudio; }; } From f8723d92f6044cb3696e2e2bfff514f22ae4835e Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Tue, 1 Dec 2015 13:45:42 -0800 Subject: [PATCH 028/102] Bug 1210319 - p1. Removed unused code - r=rillian --HG-- extra : commitid : 6LU02OBJDFK --- media/libstagefright/binding/MP4Metadata.cpp | 8 +- .../include/media/stagefright/MediaSource.h | 74 - .../media/libstagefright/MPEG4Extractor.cpp | 1828 +---------------- .../av/media/libstagefright/MediaSource.cpp | 39 - .../libstagefright/include/MPEG4Extractor.h | 7 - 5 files changed, 6 insertions(+), 1950 deletions(-) diff --git a/media/libstagefright/binding/MP4Metadata.cpp b/media/libstagefright/binding/MP4Metadata.cpp index a3f68b73b19a..eebd36866b57 100644 --- a/media/libstagefright/binding/MP4Metadata.cpp +++ b/media/libstagefright/binding/MP4Metadata.cpp @@ -242,7 +242,7 @@ MP4Metadata::CheckTrack(const char* aMimeType, int32_t aIndex) const { sp track = mPrivate->mMetadataExtractor->getTrack(aIndex); - if (!track.get() || track->start() != OK) { + if (!track.get()) { return nullptr; } @@ -258,8 +258,6 @@ MP4Metadata::CheckTrack(const char* aMimeType, e = Move(info); } - track->stop(); - if (e && e->IsValid()) { return e; } @@ -296,7 +294,7 @@ MP4Metadata::ReadTrackIndex(FallibleTArray& aDest, mozilla::Track return false; } sp track = mPrivate->mMetadataExtractor->getTrack(trackNumber); - if (!track.get() || track->start() != OK) { + if (!track.get()) { return false; } sp metadata = @@ -307,8 +305,6 @@ MP4Metadata::ReadTrackIndex(FallibleTArray& aDest, mozilla::Track } bool rv = ConvertIndex(aDest, track->exportIndex(), mediaTime); - track->stop(); - return rv; } diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h index aebab442f8d2..b9296d017144 100644 --- a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h @@ -33,83 +33,9 @@ class MetaData; struct MediaSource : public virtual RefBase { MediaSource(); - // To be called before any other methods on this object, except - // getFormat(). - virtual status_t start(MetaData *params = NULL) = 0; - - // Any blocking read call returns immediately with a result of NO_INIT. - // It is an error to call any methods other than start after this call - // returns. Any buffers the object may be holding onto at the time of - // the stop() call are released. - // Also, it is imperative that any buffers output by this object and - // held onto by callers be released before a call to stop() !!! - virtual status_t stop() = 0; - // Returns the format of the data output by this media source. virtual sp getFormat() = 0; - struct ReadOptions; - - // Returns a new buffer of data. Call blocks until a - // buffer is available, an error is encountered of the end of the stream - // is reached. - // End of stream is signalled by a result of ERROR_END_OF_STREAM. - // A result of INFO_FORMAT_CHANGED indicates that the format of this - // MediaSource has changed mid-stream, the client can continue reading - // but should be prepared for buffers of the new configuration. - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options = NULL) = 0; - - // Options that modify read() behaviour. The default is to - // a) not request a seek - // b) not be late, i.e. lateness_us = 0 - struct ReadOptions { - enum SeekMode { - SEEK_PREVIOUS_SYNC, - SEEK_NEXT_SYNC, - SEEK_CLOSEST_SYNC, - SEEK_CLOSEST, - }; - - ReadOptions(); - - // Reset everything back to defaults. - void reset(); - - void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC); - void clearSeekTo(); - bool getSeekTo(int64_t *time_us, SeekMode *mode) const; - - void setLateBy(int64_t lateness_us); - int64_t getLateBy() const; - - private: - enum Options { - kSeekTo_Option = 1, - }; - - uint32_t mOptions; - int64_t mSeekTimeUs; - SeekMode mSeekMode; - int64_t mLatenessUs; - }; - - // Causes this source to suspend pulling data from its upstream source - // until a subsequent read-with-seek. Currently only supported by - // OMXCodec. - virtual status_t pause() { - return ERROR_UNSUPPORTED; - } - - // The consumer of this media source requests that the given buffers - // are to be returned exclusively in response to read calls. - // This will be called after a successful start() and before the - // first read() call. - // Callee assumes ownership of the buffers if no error is returned. - virtual status_t setBuffers(const Vector &buffers) { - return ERROR_UNSUPPORTED; - } - struct Indice { uint64_t start_offset; diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index a1caf72fe3f9..84ea78b4aa50 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -46,133 +46,24 @@ namespace stagefright { class MPEG4Source : public MediaSource { public: - // Caller retains ownership of both "dataSource" and "sampleTable". MPEG4Source(const sp &format, - const sp &dataSource, uint32_t timeScale, - const sp &sampleTable, - nsTArray &sidx, - MPEG4Extractor::TrackExtends &trackExtends); - - virtual status_t start(MetaData *params = NULL); - virtual status_t stop(); + const sp &sampleTable); virtual sp getFormat(); - virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); - virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL); virtual nsTArray exportIndex(); protected: virtual ~MPEG4Source(); private: - Mutex mLock; - sp mFormat; - sp mDataSource; uint32_t mTimescale; sp mSampleTable; - uint32_t mCurrentSampleIndex; - uint32_t mCurrentFragmentIndex; - nsTArray &mSegments; - bool mLookedForMoof; - off64_t mFirstMoofOffset; - off64_t mCurrentMoofOffset; - off64_t mNextMoofOffset; - uint32_t mCurrentTime; - int32_t mLastParsedTrackId; - int32_t mTrackId; - - int32_t mCryptoMode; // passed in from extractor - int32_t mDefaultIVSize; // passed in from extractor - uint8_t mCryptoKey[16]; // passed in from extractor - uint32_t mCurrentAuxInfoType; - uint32_t mCurrentAuxInfoTypeParameter; - int32_t mCurrentDefaultSampleInfoSize; - uint32_t mCurrentSampleInfoCount; - uint32_t mCurrentSampleInfoAllocSize; - uint8_t* mCurrentSampleInfoSizes; - uint32_t mCurrentSampleInfoOffsetCount; - uint32_t mCurrentSampleInfoOffsetsAllocSize; - uint64_t* mCurrentSampleInfoOffsets; - - bool mIsAVC; - size_t mNALLengthSize; - - bool mStarted; - - MediaBuffer *mBuffer; - - bool mWantsNALFragments; - - uint8_t *mSrcBuffer; - FallibleTArray mSrcBackend; - - size_t parseNALSize(const uint8_t *data) const; - status_t parseChunk(off64_t *offset); - status_t parseTrackFragmentData(off64_t offset, off64_t size); - status_t parseTrackFragmentHeader(off64_t offset, off64_t size); - status_t parseTrackFragmentRun(off64_t offset, off64_t size); - status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size); - status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size); - status_t lookForMoof(); - status_t moveToNextFragment(); - - struct TrackFragmentHeaderInfo { - enum Flags { - kBaseDataOffsetPresent = 0x01, - kSampleDescriptionIndexPresent = 0x02, - kDefaultSampleDurationPresent = 0x08, - kDefaultSampleSizePresent = 0x10, - kDefaultSampleFlagsPresent = 0x20, - kDurationIsEmpty = 0x10000, - }; - - uint32_t mTrackID; - uint32_t mFlags; - uint64_t mBaseDataOffset; - uint32_t mSampleDescriptionIndex; - uint32_t mDefaultSampleDuration; - uint32_t mDefaultSampleSize; - uint32_t mDefaultSampleFlags; - - uint64_t mDataOffset; - }; - TrackFragmentHeaderInfo mTrackFragmentHeaderInfo; - - struct Sample { - off64_t offset; - uint32_t size; - uint32_t duration; - int32_t ctsOffset; - uint32_t flags; - uint8_t iv[16]; - nsTArray clearsizes; - nsTArray encryptedsizes; - - bool isSync() const { return !(flags & 0x1010000); } - }; - nsTArray mCurrentSamples; - MPEG4Extractor::TrackExtends mTrackExtends; - - // XXX hack -- demuxer expects a track's trun to be seen before saio or - // saiz. Here we store saiz/saio box offsets for parsing *after* the trun - // has been parsed. - struct AuxRange { - off64_t mStart; - off64_t mSize; - }; - nsTArray mDeferredSaiz; - nsTArray mDeferredSaio; MPEG4Source(const MPEG4Source &); MPEG4Source &operator=(const MPEG4Source &); - - bool ensureSrcBufferAllocated(int32_t size); - // Ensure that we have enough data in mMediaBuffer to copy our data. - // Returns false if not and clear mMediaBuffer. - bool ensureMediaBufferAllocated(int32_t size); }; // This custom data source wraps an existing one and satisfies requests @@ -2579,9 +2470,7 @@ sp MPEG4Extractor::getTrack(size_t index) { ALOGV("getTrack called, pssh: %d", mPssh.Length()); - return new MPEG4Source( - track->meta, mDataSource, track->timescale, track->sampleTable, - mSidxEntries, mTrackExtends); + return new MPEG4Source(track->meta, track->timescale, track->sampleTable); } // static @@ -2788,1529 +2677,20 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( MPEG4Source::MPEG4Source( const sp &format, - const sp &dataSource, uint32_t timeScale, - const sp &sampleTable, - nsTArray &sidx, - MPEG4Extractor::TrackExtends &trackExtends) + const sp &sampleTable) : mFormat(format), - mDataSource(dataSource), mTimescale(timeScale), - mSampleTable(sampleTable), - mCurrentSampleIndex(0), - mCurrentFragmentIndex(0), - mSegments(sidx), - mLookedForMoof(false), - mFirstMoofOffset(0), - mCurrentMoofOffset(0), - mCurrentTime(0), - mCurrentSampleInfoAllocSize(0), - mCurrentSampleInfoSizes(NULL), - mCurrentSampleInfoOffsetsAllocSize(0), - mCurrentSampleInfoOffsets(NULL), - mIsAVC(false), - mNALLengthSize(0), - mStarted(false), - mBuffer(NULL), - mWantsNALFragments(false), - mSrcBuffer(NULL), - mTrackExtends(trackExtends) { - - mFormat->findInt32(kKeyCryptoMode, &mCryptoMode); - mDefaultIVSize = 0; - mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize); - uint32_t keytype; - const void *key; - size_t keysize; - if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) { - CHECK(keysize <= 16); - memset(mCryptoKey, 0, 16); - memcpy(mCryptoKey, key, keysize); - } - - const char *mime; - bool success = mFormat->findCString(kKeyMIMEType, &mime); - CHECK(success); - - mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); - - if (mIsAVC) { - uint32_t type; - const void *data; - size_t size; - CHECK(format->findData(kKeyAVCC, &type, &data, &size)); - - const uint8_t *ptr = (const uint8_t *)data; - - CHECK(size >= 7); - CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 - - // The number of bytes used to encode the length of a NAL unit. - mNALLengthSize = 1 + (ptr[4] & 3); - } - - CHECK(format->findInt32(kKeyTrackID, &mTrackId)); + mSampleTable(sampleTable) { } MPEG4Source::~MPEG4Source() { - if (mStarted) { - stop(); - } - free(mCurrentSampleInfoSizes); - free(mCurrentSampleInfoOffsets); -} - -status_t MPEG4Source::start(MetaData *params) { - Mutex::Autolock autoLock(mLock); - - CHECK(!mStarted); - - int32_t val; - if (params && params->findInt32(kKeyWantsNALFragments, &val) - && val != 0) { - mWantsNALFragments = true; - } else { - mWantsNALFragments = false; - } - - mSrcBuffer = mSrcBackend.Elements(); - - mStarted = true; - - return OK; -} - -status_t MPEG4Source::stop() { - Mutex::Autolock autoLock(mLock); - - CHECK(mStarted); - - if (mBuffer != NULL) { - mBuffer->release(); - mBuffer = NULL; - } - - mSrcBackend.Clear(); - - mStarted = false; - mCurrentSampleIndex = 0; - - return OK; -} - -status_t MPEG4Source::parseChunk(off64_t *offset) { - uint32_t hdr[2]; - if (mDataSource->readAt(*offset, hdr, 8) < 8) { - return ERROR_IO; - } - uint64_t chunk_size = ntohl(hdr[0]); - uint32_t chunk_type = ntohl(hdr[1]); - off64_t data_offset = *offset + 8; - - if (chunk_size == 1) { - if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) { - return ERROR_IO; - } - chunk_size = ntoh64(chunk_size); - data_offset += 8; - - if (chunk_size < 16) { - // The smallest valid chunk is 16 bytes long in this case. - return ERROR_MALFORMED; - } - } else if (chunk_size < 8) { - // The smallest valid chunk is 8 bytes long. - return ERROR_MALFORMED; - } - - if (chunk_size >= kMAX_ALLOCATION) { - // Could cause an overflow later. Abort. - return ERROR_MALFORMED; - } - - char chunk[5]; - MakeFourCCString(chunk_type, chunk); - ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset); - - off64_t chunk_data_size = *offset + chunk_size - data_offset; - - switch(chunk_type) { - - case FOURCC('t', 'r', 'a', 'f'): - case FOURCC('m', 'o', 'o', 'f'): { - off64_t stop_offset = *offset + chunk_size; - *offset = data_offset; - while (*offset < stop_offset) { - status_t err = parseChunk(offset); - if (err != OK) { - return err; - } - } - if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { - // *offset points to the mdat box following this moof - parseChunk(offset); // doesn't actually parse it, just updates offset - mNextMoofOffset = *offset; - } - break; - } - - case FOURCC('t', 'f', 'h', 'd'): { - status_t err; - if ((err = parseTrackFragmentHeader(data_offset, chunk_data_size)) != OK) { - return err; - } - *offset += chunk_size; - break; - } - - case FOURCC('t', 'f', 'd', 't'): - { - status_t err; - if (mLastParsedTrackId == mTrackId) { - if ((err = parseTrackFragmentData(data_offset, chunk_data_size)) != OK) { - return err; - } - } - *offset += chunk_size; - break; - } - - case FOURCC('t', 'r', 'u', 'n'): { - status_t err; - if (mLastParsedTrackId == mTrackId) { - if ((err = parseTrackFragmentRun(data_offset, chunk_data_size)) != OK) { - return err; - } - } - - *offset += chunk_size; - break; - } - - case FOURCC('s', 'a', 'i', 'z'): { - status_t err; - if (mLastParsedTrackId == mTrackId) { - if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) { - return err; - } - } - *offset += chunk_size; - break; - } - case FOURCC('s', 'a', 'i', 'o'): { - status_t err; - if (mLastParsedTrackId == mTrackId) { - if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) { - return err; - } - } - *offset += chunk_size; - break; - } - - case FOURCC('m', 'd', 'a', 't'): { - // parse DRM info if present - ALOGV("MPEG4Source::parseChunk mdat"); - // if saiz/saoi was previously observed, do something with the sampleinfos - *offset += chunk_size; - break; - } - - default: { - *offset += chunk_size; - break; - } - } - return OK; -} - -status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) { - ALOGV("parseSampleAuxiliaryInformationSizes"); - // 14496-12 8.7.12 - - if (mCurrentSamples.IsEmpty()) { - // XXX hack -- we haven't seen trun yet; defer parsing this box until - // after trun. - ALOGW("deferring processing of saiz box"); - AuxRange range; - range.mStart = offset; - range.mSize = size; - mDeferredSaiz.AppendElement(range); - return OK; - } - - uint8_t version; - if (mDataSource->readAt( - offset, &version, sizeof(version)) - < (ssize_t)sizeof(version)) { - return ERROR_IO; - } - - if (version != 0) { - return ERROR_UNSUPPORTED; - } - offset++; - - uint32_t flags; - if (!mDataSource->getUInt24(offset, &flags)) { - return ERROR_IO; - } - offset += 3; - - if (flags & 1) { - uint32_t tmp; - if (!mDataSource->getUInt32(offset, &tmp)) { - return ERROR_MALFORMED; - } - mCurrentAuxInfoType = tmp; - offset += 4; - if (!mDataSource->getUInt32(offset, &tmp)) { - return ERROR_MALFORMED; - } - mCurrentAuxInfoTypeParameter = tmp; - offset += 4; - } - - uint8_t defsize; - if (mDataSource->readAt(offset, &defsize, 1) != 1) { - return ERROR_MALFORMED; - } - mCurrentDefaultSampleInfoSize = defsize; - offset++; - - uint32_t smplcnt; - if (!mDataSource->getUInt32(offset, &smplcnt)) { - return ERROR_MALFORMED; - } - mCurrentSampleInfoCount = smplcnt; - offset += 4; - - if (mCurrentDefaultSampleInfoSize != 0) { - ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize); - return OK; - } - if (smplcnt > mCurrentSampleInfoAllocSize) { - mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt); - mCurrentSampleInfoAllocSize = smplcnt; - } - - mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt); - return OK; -} - -status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) { - ALOGV("parseSampleAuxiliaryInformationOffsets"); - // 14496-12 8.7.13 - - if (mCurrentSamples.IsEmpty()) { - // XXX hack -- we haven't seen trun yet; defer parsing this box until - // after trun. - ALOGW("deferring processing of saio box"); - AuxRange range; - range.mStart = offset; - range.mSize = size; - mDeferredSaio.AppendElement(range); - return OK; - } - - uint8_t version; - if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) { - return ERROR_IO; - } - offset++; - - uint32_t flags; - if (!mDataSource->getUInt24(offset, &flags)) { - return ERROR_IO; - } - offset += 3; - - if (flags & 1) { - // Skip uint32s aux_info_type and aux_info_type_parameter - offset += 8; - } - - uint32_t entrycount; - if (!mDataSource->getUInt32(offset, &entrycount)) { - return ERROR_IO; - } - offset += 4; - - if (entrycount > mCurrentSampleInfoOffsetsAllocSize) { - mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8); - mCurrentSampleInfoOffsetsAllocSize = entrycount; - } - mCurrentSampleInfoOffsetCount = entrycount; - - for (size_t i = 0; i < entrycount; i++) { - if (version == 0) { - uint32_t tmp; - if (!mDataSource->getUInt32(offset, &tmp)) { - return ERROR_IO; - } - mCurrentSampleInfoOffsets[i] = tmp; - offset += 4; - } else { - uint64_t tmp; - if (!mDataSource->getUInt64(offset, &tmp)) { - return ERROR_IO; - } - mCurrentSampleInfoOffsets[i] = tmp; - offset += 8; - } - } - - // parse clear/encrypted data - - off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof - - drmoffset += mCurrentMoofOffset; - int ivlength; - CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength)); - - // read CencSampleAuxiliaryDataFormats - for (size_t i = 0; i < mCurrentSampleInfoCount; i++) { - Sample *smpl = &mCurrentSamples[i]; - - memset(smpl->iv, 0, 16); - if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) { - return ERROR_IO; - } - - drmoffset += ivlength; - - int32_t smplinfosize = mCurrentDefaultSampleInfoSize; - if (smplinfosize == 0) { - smplinfosize = mCurrentSampleInfoSizes[i]; - } - if (smplinfosize > ivlength) { - uint16_t numsubsamples; - if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) { - return ERROR_IO; - } - drmoffset += 2; - for (size_t j = 0; j < numsubsamples; j++) { - uint16_t numclear; - uint32_t numencrypted; - if (!mDataSource->getUInt16(drmoffset, &numclear)) { - return ERROR_IO; - } - drmoffset += 2; - if (!mDataSource->getUInt32(drmoffset, &numencrypted)) { - return ERROR_IO; - } - drmoffset += 4; - smpl->clearsizes.AppendElement(numclear); - smpl->encryptedsizes.AppendElement(numencrypted); - } - } else { - smpl->clearsizes.AppendElement(0); - smpl->encryptedsizes.AppendElement(smpl->size); - } - } - - - return OK; -} - -status_t MPEG4Source::parseTrackFragmentData(off64_t offset, off64_t size) { - - if (size < 8) { - return -EINVAL; - } - - uint32_t flags; - if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags - return ERROR_MALFORMED; - } - - uint8_t version = flags >> 24; - - if (version == 0) { - if (mDataSource->getUInt32(offset + 4, &mCurrentTime)) { - return OK; - } - } else if (version == 1) { - if (size < 12) { - return -EINVAL; - } - uint64_t time; - if (mDataSource->getUInt64(offset + 4, &time)) { - mCurrentTime = time; - return OK; - } - } - - return ERROR_MALFORMED; -} - -status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) { - - if (size < 8) { - return -EINVAL; - } - - uint32_t flags; - if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags - return ERROR_MALFORMED; - } - - if (flags & 0xff000000) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset + 4, (uint32_t*)&mLastParsedTrackId)) { - return ERROR_MALFORMED; - } - - if (mLastParsedTrackId != mTrackId) { - // this is not the right track, skip it - return OK; - } - - mTrackFragmentHeaderInfo.mFlags = flags; - mTrackFragmentHeaderInfo.mTrackID = mLastParsedTrackId; - - mTrackFragmentHeaderInfo.mBaseDataOffset = 0; - mTrackFragmentHeaderInfo.mSampleDescriptionIndex = 0; - mTrackFragmentHeaderInfo.mDefaultSampleDuration = 0; - mTrackFragmentHeaderInfo.mDefaultSampleSize = 0; - mTrackFragmentHeaderInfo.mDefaultSampleFlags = 0; - mTrackFragmentHeaderInfo.mDataOffset = 0; - - offset += 8; - size -= 8; - - ALOGV("fragment header: %08x %08x", flags, mTrackFragmentHeaderInfo.mTrackID); - - if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) { - if (size < 8) { - return -EINVAL; - } - - if (!mDataSource->getUInt64(offset, &mTrackFragmentHeaderInfo.mBaseDataOffset)) { - return ERROR_MALFORMED; - } - offset += 8; - size -= 8; - } - - if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) { - if (size < 4) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mSampleDescriptionIndex)) { - return ERROR_MALFORMED; - } - offset += 4; - size -= 4; - } - - if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { - if (size < 4) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleDuration)) { - return ERROR_MALFORMED; - } - offset += 4; - size -= 4; - } - - if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { - if (size < 4) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleSize)) { - return ERROR_MALFORMED; - } - offset += 4; - size -= 4; - } - - if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { - if (size < 4) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleFlags)) { - return ERROR_MALFORMED; - } - offset += 4; - size -= 4; - } - - if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) { - mTrackFragmentHeaderInfo.mBaseDataOffset = mCurrentMoofOffset; - } - - mTrackFragmentHeaderInfo.mDataOffset = 0; - return OK; -} - -status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { - - ALOGV("MPEG4Extractor::parseTrackFragmentRun"); - if (size < 8) { - return -EINVAL; - } - - enum { - kDataOffsetPresent = 0x01, - kFirstSampleFlagsPresent = 0x04, - kSampleDurationPresent = 0x100, - kSampleSizePresent = 0x200, - kSampleFlagsPresent = 0x400, - kSampleCompositionTimeOffsetPresent = 0x800, - }; - - uint32_t flags; - if (!mDataSource->getUInt32(offset, &flags)) { - return ERROR_MALFORMED; - } - uint8_t version = flags >> 24; - ALOGV("fragment run flags: %08x", flags); - - if (version > 1) { - return -EINVAL; - } - - if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) { - // These two shall not be used together. - return -EINVAL; - } - - uint32_t sampleCount; - if (!mDataSource->getUInt32(offset + 4, &sampleCount)) { - return ERROR_MALFORMED; - } - offset += 8; - size -= 8; - - uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset; - - uint32_t firstSampleFlags = 0; - - if (flags & kDataOffsetPresent) { - if (size < 4) { - return -EINVAL; - } - - int32_t dataOffsetDelta; - if (!mDataSource->getUInt32(offset, (uint32_t*)&dataOffsetDelta)) { - return ERROR_MALFORMED; - } - - dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta; - - offset += 4; - size -= 4; - } - - if (flags & kFirstSampleFlagsPresent) { - if (size < 4) { - return -EINVAL; - } - - if (!mDataSource->getUInt32(offset, &firstSampleFlags)) { - return ERROR_MALFORMED; - } - offset += 4; - size -= 4; - } - - uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0, - sampleCtsOffset = 0; - - size_t bytesPerSample = 0; - if (flags & kSampleDurationPresent) { - bytesPerSample += 4; - } else if (mTrackFragmentHeaderInfo.mFlags - & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { - sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration; - } else { - sampleDuration = mTrackExtends.mDefaultSampleDuration; - } - - if (flags & kSampleSizePresent) { - bytesPerSample += 4; - } else if (mTrackFragmentHeaderInfo.mFlags - & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { - sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize; - } else { - sampleSize = mTrackExtends.mDefaultSampleSize; - } - - if (flags & kSampleFlagsPresent) { - bytesPerSample += 4; - } else if (mTrackFragmentHeaderInfo.mFlags - & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { - sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags; - } else { - sampleFlags = mTrackExtends.mDefaultSampleFlags; - } - - if (flags & kSampleCompositionTimeOffsetPresent) { - bytesPerSample += 4; - } else { - sampleCtsOffset = 0; - } - - if (size < sampleCount * bytesPerSample) { - return -EINVAL; - } - - Sample tmp; - for (uint32_t i = 0; i < sampleCount; ++i) { - if (flags & kSampleDurationPresent) { - if (!mDataSource->getUInt32(offset, &sampleDuration)) { - return ERROR_MALFORMED; - } - offset += 4; - } - - if (flags & kSampleSizePresent) { - if (!mDataSource->getUInt32(offset, &sampleSize)) { - return ERROR_MALFORMED; - } - offset += 4; - } - - if (flags & kSampleFlagsPresent) { - if (!mDataSource->getUInt32(offset, &sampleFlags)) { - return ERROR_MALFORMED; - } - offset += 4; - } - - if (flags & kSampleCompositionTimeOffsetPresent) { - if (!mDataSource->getUInt32(offset, &sampleCtsOffset)) { - return ERROR_MALFORMED; - } - offset += 4; - } - - ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, " - " flags 0x%08x", i + 1, - dataOffset, sampleSize, sampleDuration, - (flags & kFirstSampleFlagsPresent) && i == 0 - ? firstSampleFlags : sampleFlags); - tmp.flags = (flags & kFirstSampleFlagsPresent) && i == 0 - ? firstSampleFlags : sampleFlags; - tmp.offset = dataOffset; - tmp.size = sampleSize; - tmp.duration = sampleDuration; - tmp.ctsOffset = (int32_t)sampleCtsOffset; - mCurrentSamples.AppendElement(tmp); - - dataOffset += sampleSize; - } - - mTrackFragmentHeaderInfo.mDataOffset = dataOffset; - - for (size_t i = 0; i < mDeferredSaio.Length() && i < mDeferredSaiz.Length(); i++) { - const auto& saio = mDeferredSaio[i]; - const auto& saiz = mDeferredSaiz[i]; - parseSampleAuxiliaryInformationSizes(saiz.mStart, saiz.mSize); - parseSampleAuxiliaryInformationOffsets(saio.mStart, saio.mSize); - } - - return OK; } sp MPEG4Source::getFormat() { - Mutex::Autolock autoLock(mLock); - return mFormat; } -size_t MPEG4Source::parseNALSize(const uint8_t *data) const { - switch (mNALLengthSize) { - case 1: - return *data; - case 2: - return U16_AT(data); - case 3: - return ((size_t)data[0] << 16) | U16_AT(&data[1]); - case 4: - return U32_AT(data); - } - - // This cannot happen, mNALLengthSize springs to life by adding 1 to - // a 2-bit integer. - CHECK(!"Should not be here."); - - return 0; -} - -status_t MPEG4Source::lookForMoof() { - off64_t offset = 0; - off64_t size; - while (true) { - uint32_t hdr[2]; - auto x = mDataSource->readAt(offset, hdr, 8); - if (x < 8) { - return NOT_ENOUGH_DATA; - } - uint32_t chunk_size = ntohl(hdr[0]); - uint32_t chunk_type = ntohl(hdr[1]); - char chunk[5]; - MakeFourCCString(chunk_type, chunk); - if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { - mFirstMoofOffset = mCurrentMoofOffset = offset; - parseChunk(&offset); - return OK; - } - if (chunk_type == FOURCC('m', 'd', 'a', 't')) { - return OK; - } - offset += chunk_size; - } -} - -bool MPEG4Source::ensureSrcBufferAllocated(int32_t aSize) { - if (mSrcBackend.Length() >= aSize) { - return true; - } - if (!mSrcBackend.SetLength(aSize, mozilla::fallible)) { - ALOGE("Error insufficient memory, requested %u bytes (had:%u)", - aSize, mSrcBackend.Length()); - return false; - } - mSrcBuffer = mSrcBackend.Elements(); - return true; -} - -bool MPEG4Source::ensureMediaBufferAllocated(int32_t aSize) { - if (mBuffer->ensuresize(aSize)) { - return true; - } - ALOGE("Error insufficient memory, requested %u bytes (had:%u)", - aSize, mBuffer->size()); - mBuffer->release(); - mBuffer = NULL; - return false; -} - -status_t MPEG4Source::read( - MediaBuffer **out, const ReadOptions *options) { - Mutex::Autolock autoLock(mLock); - - CHECK(mStarted); - - if (!mLookedForMoof) { - mLookedForMoof = lookForMoof() == OK; - } - - if (mFirstMoofOffset > 0) { - return fragmentedRead(out, options); - } - - *out = NULL; - - int64_t targetSampleTimeUs = -1; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - uint32_t findFlags = 0; - switch (mode) { - case ReadOptions::SEEK_PREVIOUS_SYNC: - findFlags = SampleTable::kFlagBefore; - break; - case ReadOptions::SEEK_NEXT_SYNC: - findFlags = SampleTable::kFlagAfter; - break; - case ReadOptions::SEEK_CLOSEST_SYNC: - case ReadOptions::SEEK_CLOSEST: - findFlags = SampleTable::kFlagClosest; - break; - default: - CHECK(!"Should not be here."); - break; - } - - uint32_t sampleIndex; - status_t err = mSampleTable->findSampleAtTime( - seekTimeUs * mTimescale / 1000000, - &sampleIndex, findFlags); - - if (mode == ReadOptions::SEEK_CLOSEST) { - // We found the closest sample already, now we want the sync - // sample preceding it (or the sample itself of course), even - // if the subsequent sync sample is closer. - findFlags = SampleTable::kFlagBefore; - } - - uint32_t syncSampleIndex; - if (err == OK) { - err = mSampleTable->findSyncSampleNear( - sampleIndex, &syncSampleIndex, findFlags); - } - - uint32_t sampleTime; - if (err == OK) { - err = mSampleTable->getMetaDataForSample( - sampleIndex, NULL, NULL, &sampleTime); - } - - if (err != OK) { - if (err == ERROR_OUT_OF_RANGE) { - // An attempt to seek past the end of the stream would - // normally cause this ERROR_OUT_OF_RANGE error. Propagating - // this all the way to the MediaPlayer would cause abnormal - // termination. Legacy behaviour appears to be to behave as if - // we had seeked to the end of stream, ending normally. - err = ERROR_END_OF_STREAM; - } - ALOGV("end of stream"); - return err; - } - - if (mode == ReadOptions::SEEK_CLOSEST) { - if (!mTimescale) { - return ERROR_MALFORMED; - } - targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale; - } - -#if 0 - uint32_t syncSampleTime; - CHECK_EQ(OK, mSampleTable->getMetaDataForSample( - syncSampleIndex, NULL, NULL, &syncSampleTime)); - - ALOGI("seek to time %lld us => sample at time %lld us, " - "sync sample at time %lld us", - seekTimeUs, - sampleTime * 1000000ll / mTimescale, - syncSampleTime * 1000000ll / mTimescale); -#endif - - mCurrentSampleIndex = syncSampleIndex; - if (mBuffer != NULL) { - mBuffer->release(); - mBuffer = NULL; - } - - // fall through - } - - off64_t offset = 0; - size_t size = 0; - uint32_t dts = 0; - uint32_t cts = 0; - uint32_t duration = 0; - bool isSyncSample = false; - bool newBuffer = false; - if (mBuffer == NULL) { - newBuffer = true; - - status_t err = - mSampleTable->getMetaDataForSample( - mCurrentSampleIndex, &offset, &size, &cts, &duration, - &isSyncSample, &dts); - - if (err != OK) { - return err; - } - - int32_t max_size; - CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size)); - mBuffer = new MediaBuffer(std::min(max_size, 1024 * 1024)); - } - - if (!mIsAVC || mWantsNALFragments) { - if (newBuffer) { - if (!ensureMediaBufferAllocated(size)) { - return ERROR_MALFORMED; - } - ssize_t num_bytes_read = - mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); - - if (num_bytes_read < (ssize_t)size) { - mBuffer->release(); - mBuffer = NULL; - - return ERROR_IO; - } - - CHECK(mBuffer != NULL); - mBuffer->set_range(0, size); - mBuffer->meta_data()->clear(); - mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset); - if (!mTimescale) { - return ERROR_MALFORMED; - } - mBuffer->meta_data()->setInt64( - kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyDuration, ((int64_t)duration * 1000000) / mTimescale); - - if (targetSampleTimeUs >= 0) { - mBuffer->meta_data()->setInt64( - kKeyTargetTime, targetSampleTimeUs); - } - - if (isSyncSample) { - mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - - if (mSampleTable->hasCencInfo()) { - nsTArray clearSizes; - nsTArray cipherSizes; - uint8_t iv[16]; - status_t err = mSampleTable->getSampleCencInfo( - mCurrentSampleIndex, clearSizes, cipherSizes, iv); - - if (err != OK) { - return err; - } - - const auto& meta = mBuffer->meta_data(); - meta->setData(kKeyPlainSizes, 0, clearSizes.Elements(), - clearSizes.Length() * sizeof(uint16_t)); - meta->setData(kKeyEncryptedSizes, 0, cipherSizes.Elements(), - cipherSizes.Length() * sizeof(uint32_t)); - meta->setData(kKeyCryptoIV, 0, iv, sizeof(iv)); - meta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); - meta->setInt32(kKeyCryptoMode, mCryptoMode); - meta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); - } - - ++mCurrentSampleIndex; - } - - if (!mIsAVC) { - *out = mBuffer; - mBuffer = NULL; - - return OK; - } - - // Each NAL unit is split up into its constituent fragments and - // each one of them returned in its own buffer. - - if (mBuffer->range_length() < mNALLengthSize) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return ERROR_MALFORMED; - } - - const uint8_t *src = - (const uint8_t *)mBuffer->data() + mBuffer->range_offset(); - - size_t nal_size = parseNALSize(src); - if (mBuffer->range_length() < mNALLengthSize + nal_size) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return ERROR_MALFORMED; - } - - MediaBuffer *clone = mBuffer->clone(); - CHECK(clone != NULL); - clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size); - - CHECK(mBuffer != NULL); - mBuffer->set_range( - mBuffer->range_offset() + mNALLengthSize + nal_size, - mBuffer->range_length() - mNALLengthSize - nal_size); - - if (mBuffer->range_length() == 0) { - mBuffer->release(); - mBuffer = NULL; - } - - *out = clone; - - return OK; - } else { - // Whole NAL units are returned but each fragment is prefixed by - // the NAL length, stored in four bytes. - ssize_t num_bytes_read = 0; - int32_t drm = 0; - bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0); - if (usesDRM) { - if (!ensureMediaBufferAllocated(size)) { - return ERROR_MALFORMED; - } - num_bytes_read = - mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); - } else { - if (!ensureSrcBufferAllocated(size)) { - return ERROR_MALFORMED; - } - num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); - } - - if (num_bytes_read < (ssize_t)size) { - mBuffer->release(); - mBuffer = NULL; - - return ERROR_IO; - } - - if (usesDRM) { - CHECK(mBuffer != NULL); - mBuffer->set_range(0, size); - - } else { - size_t srcOffset = 0; - size_t dstOffset = 0; - - while (srcOffset < size) { - bool isMalFormed = (srcOffset + mNALLengthSize > size); - size_t nalLength = 0; - if (!isMalFormed) { - nalLength = parseNALSize(&mSrcBuffer[srcOffset]); - srcOffset += mNALLengthSize; - isMalFormed = srcOffset + nalLength > size; - } - - if (isMalFormed) { - ALOGE("Video is malformed"); - mBuffer->release(); - mBuffer = NULL; - return ERROR_MALFORMED; - } - - if (nalLength == 0) { - continue; - } - - if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) { - return ERROR_MALFORMED; - } - uint8_t *dstData = (uint8_t *)mBuffer->data(); - dstData[dstOffset++] = (uint8_t) (nalLength >> 24); - dstData[dstOffset++] = (uint8_t) (nalLength >> 16); - dstData[dstOffset++] = (uint8_t) (nalLength >> 8); - dstData[dstOffset++] = (uint8_t) nalLength; - memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength); - srcOffset += nalLength; - dstOffset += nalLength; - } - CHECK_EQ(srcOffset, size); - CHECK(mBuffer != NULL); - mBuffer->set_range(0, dstOffset); - } - - mBuffer->meta_data()->clear(); - mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset); - if (!mTimescale) { - return ERROR_MALFORMED; - } - mBuffer->meta_data()->setInt64( - kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyDuration, ((int64_t)duration * 1000000) / mTimescale); - - if (targetSampleTimeUs >= 0) { - mBuffer->meta_data()->setInt64( - kKeyTargetTime, targetSampleTimeUs); - } - - if (isSyncSample) { - mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - - if (mSampleTable->hasCencInfo()) { - nsTArray clearSizes; - nsTArray cipherSizes; - uint8_t iv[16]; - status_t err = mSampleTable->getSampleCencInfo( - mCurrentSampleIndex, clearSizes, cipherSizes, iv); - - if (err != OK) { - return err; - } - - const auto& meta = mBuffer->meta_data(); - meta->setData(kKeyPlainSizes, 0, clearSizes.Elements(), - clearSizes.Length() * sizeof(uint16_t)); - meta->setData(kKeyEncryptedSizes, 0, cipherSizes.Elements(), - cipherSizes.Length() * sizeof(uint32_t)); - meta->setData(kKeyCryptoIV, 0, iv, sizeof(iv)); - meta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); - meta->setInt32(kKeyCryptoMode, mCryptoMode); - meta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); - } - - ++mCurrentSampleIndex; - - *out = mBuffer; - mBuffer = NULL; - - return OK; - } -} - -status_t MPEG4Source::moveToNextFragment() { - off64_t nextMoof = mNextMoofOffset; - mCurrentSamples.Clear(); - mDeferredSaio.Clear(); - mDeferredSaiz.Clear(); - mCurrentSampleIndex = 0; - uint32_t hdr[2]; - do { - if (mDataSource->readAt(nextMoof, hdr, 8) < 8) { - return ERROR_END_OF_STREAM; - } - uint64_t chunk_size = ntohl(hdr[0]); - uint32_t chunk_type = ntohl(hdr[1]); - - // Skip over anything that isn't a moof - if (chunk_type != FOURCC('m', 'o', 'o', 'f')) { - nextMoof += chunk_size; - continue; - } - mCurrentMoofOffset = nextMoof; - status_t ret = parseChunk(&nextMoof); - if (ret != OK) { - return ret; - } - } while (mCurrentSamples.Length() == 0); - return OK; -} - -status_t MPEG4Source::fragmentedRead( - MediaBuffer **out, const ReadOptions *options) { - - ALOGV("MPEG4Source::fragmentedRead"); - - CHECK(mStarted); - - *out = NULL; - - int64_t targetSampleTimeUs = -1; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - - int numSidxEntries = mSegments.Length(); - if (numSidxEntries != 0) { - int64_t totalTime = 0; - off64_t totalOffset = mFirstMoofOffset; - for (int i = 0; i < numSidxEntries; i++) { - const SidxEntry *se = &mSegments[i]; - if (totalTime + se->mDurationUs > seekTimeUs) { - // The requested time is somewhere in this segment - if ((mode == ReadOptions::SEEK_NEXT_SYNC) || - (mode == ReadOptions::SEEK_CLOSEST_SYNC && - (seekTimeUs - totalTime) > (totalTime + se->mDurationUs - seekTimeUs))) { - // requested next sync, or closest sync and it was closer to the end of - // this segment - totalTime += se->mDurationUs; - totalOffset += se->mSize; - } - break; - } - totalTime += se->mDurationUs; - totalOffset += se->mSize; - } - mCurrentMoofOffset = totalOffset; - mCurrentSamples.Clear(); - mDeferredSaio.Clear(); - mDeferredSaiz.Clear(); - mCurrentSampleIndex = 0; - mCurrentTime = totalTime * mTimescale / 1000000ll; - mNextMoofOffset = totalOffset; - } - else { - mNextMoofOffset = mFirstMoofOffset; - uint32_t seekTime = (int32_t) ((seekTimeUs * mTimescale) / 1000000ll); - while (true) { - status_t ret = moveToNextFragment(); - if (ret != OK) { - return ret; - } - uint32_t time = mCurrentTime; - int i; - for (i = 0; i < mCurrentSamples.Length() && time <= seekTime; i++) { - const Sample *smpl = &mCurrentSamples[i]; - if (smpl->isSync()) { - mCurrentSampleIndex = i; - mCurrentTime = time; - } - time += smpl->duration; - } - if (i != mCurrentSamples.Length()) { - break; - } - } - ALOGV("Seeking offset %d; %ldus\n", mCurrentTime - seekTime, - (((int64_t) mCurrentTime - seekTime) * 1000000ll) / mTimescale); - } - - if (mBuffer != NULL) { - mBuffer->release(); - mBuffer = NULL; - } - - // fall through - } - - off64_t offset = 0; - size_t size = 0; - uint32_t dts = 0; - int64_t cts = 0; - uint32_t duration = 0; - bool isSyncSample = false; - bool newBuffer = false; - if (mBuffer == NULL) { - newBuffer = true; - - if (mCurrentSampleIndex >= mCurrentSamples.Length()) { - status_t ret = moveToNextFragment(); - if (ret != OK) { - return ret; - } - } - - const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; - offset = smpl->offset; - size = smpl->size; - dts = mCurrentTime; - cts = mCurrentTime + smpl->ctsOffset; - duration = smpl->duration; - mCurrentTime += smpl->duration; - isSyncSample = smpl->isSync(); - - int32_t max_size; - CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size)); - mBuffer = new MediaBuffer(std::min(max_size, 1024 * 1024)); - } - - const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; - const sp bufmeta = mBuffer->meta_data(); - bufmeta->clear(); - if (smpl->encryptedsizes.Length()) { - // store clear/encrypted lengths in metadata - bufmeta->setData(kKeyPlainSizes, 0, - smpl->clearsizes.Elements(), smpl->clearsizes.Length() * 2); - bufmeta->setData(kKeyEncryptedSizes, 0, - smpl->encryptedsizes.Elements(), smpl->encryptedsizes.Length() * 4); - bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size? - bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); - bufmeta->setInt32(kKeyCryptoMode, mCryptoMode); - bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16); - } - - if (!mIsAVC || mWantsNALFragments) { - if (newBuffer) { - if (!ensureMediaBufferAllocated(size)) { - return ERROR_MALFORMED; - } - ssize_t num_bytes_read = - mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); - - if (num_bytes_read < (ssize_t)size) { - mBuffer->release(); - mBuffer = NULL; - - ALOGV("i/o error"); - return ERROR_IO; - } - - CHECK(mBuffer != NULL); - mBuffer->set_range(0, size); - if (!mTimescale) { - return ERROR_MALFORMED; - } - mBuffer->meta_data()->setInt64( - kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyTime, (cts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyDuration, ((int64_t)duration * 1000000) / mTimescale); - - if (targetSampleTimeUs >= 0) { - mBuffer->meta_data()->setInt64( - kKeyTargetTime, targetSampleTimeUs); - } - - if (isSyncSample) { - mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - - ++mCurrentSampleIndex; - } - - if (!mIsAVC) { - *out = mBuffer; - mBuffer = NULL; - - return OK; - } - - // Each NAL unit is split up into its constituent fragments and - // each one of them returned in its own buffer. - - if (mBuffer->range_length() < mNALLengthSize) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return ERROR_MALFORMED; - } - - const uint8_t *src = - (const uint8_t *)mBuffer->data() + mBuffer->range_offset(); - - size_t nal_size = parseNALSize(src); - if (mBuffer->range_length() < mNALLengthSize + nal_size) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return ERROR_MALFORMED; - } - - MediaBuffer *clone = mBuffer->clone(); - CHECK(clone != NULL); - clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size); - - CHECK(mBuffer != NULL); - mBuffer->set_range( - mBuffer->range_offset() + mNALLengthSize + nal_size, - mBuffer->range_length() - mNALLengthSize - nal_size); - - if (mBuffer->range_length() == 0) { - mBuffer->release(); - mBuffer = NULL; - } - - *out = clone; - - return OK; - } else { - ALOGV("whole NAL"); - // Whole NAL units are returned but each fragment is prefixed by - // the NAL unit's length, stored in four bytes. - ssize_t num_bytes_read = 0; - int32_t drm = 0; - bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0); - if (usesDRM) { - if (!ensureMediaBufferAllocated(size)) { - return ERROR_MALFORMED; - } - num_bytes_read = - mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size); - } else { - if (!ensureSrcBufferAllocated(size)) { - return ERROR_MALFORMED; - } - num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size); - } - - if (num_bytes_read < (ssize_t)size) { - mBuffer->release(); - mBuffer = NULL; - - ALOGV("i/o error"); - return ERROR_IO; - } - - if (usesDRM) { - CHECK(mBuffer != NULL); - mBuffer->set_range(0, size); - - } else { - size_t srcOffset = 0; - size_t dstOffset = 0; - - while (srcOffset < size) { - bool isMalFormed = (srcOffset + mNALLengthSize > size); - size_t nalLength = 0; - if (!isMalFormed) { - nalLength = parseNALSize(&mSrcBuffer[srcOffset]); - srcOffset += mNALLengthSize; - isMalFormed = srcOffset + nalLength > size; - } - - if (isMalFormed) { - ALOGE("Video is malformed"); - mBuffer->release(); - mBuffer = NULL; - return ERROR_MALFORMED; - } - - if (nalLength == 0) { - continue; - } - - if (!ensureMediaBufferAllocated(dstOffset + 4 + nalLength)) { - return ERROR_MALFORMED; - } - uint8_t *dstData = (uint8_t *)mBuffer->data(); - dstData[dstOffset++] = (uint8_t) (nalLength >> 24); - dstData[dstOffset++] = (uint8_t) (nalLength >> 16); - dstData[dstOffset++] = (uint8_t) (nalLength >> 8); - dstData[dstOffset++] = (uint8_t) nalLength; - memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength); - srcOffset += nalLength; - dstOffset += nalLength; - } - CHECK_EQ(srcOffset, size); - CHECK(mBuffer != NULL); - mBuffer->set_range(0, dstOffset); - } - - if (!mTimescale) { - return ERROR_MALFORMED; - } - mBuffer->meta_data()->setInt64( - kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyTime, (cts * 1000000) / mTimescale); - mBuffer->meta_data()->setInt64( - kKeyDuration, ((int64_t)duration * 1000000) / mTimescale); - - if (targetSampleTimeUs >= 0) { - mBuffer->meta_data()->setInt64( - kKeyTargetTime, targetSampleTimeUs); - } - - if (isSyncSample) { - mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - - ++mCurrentSampleIndex; - - *out = mBuffer; - mBuffer = NULL; - - return OK; - } -} - -static int compositionOrder(MediaSource::Indice* const* indice0, - MediaSource::Indice* const* indice1) -{ - if ((*indice0)->start_composition > (*indice1)->start_composition) { - return 1; - } else if ((*indice0)->start_composition == (*indice1)->start_composition) { - return 0; - } else { - return -1; - } -} - class CompositionSorter { public: @@ -4383,206 +2763,6 @@ nsTArray MPEG4Source::exportIndex() return index; } -MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( - const char *mimePrefix) { - for (Track *track = mFirstTrack; track != NULL; track = track->next) { - const char *mime; - if (track->meta != NULL - && track->meta->findCString(kKeyMIMEType, &mime) - && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) { - return track; - } - } - - return NULL; -} - -static bool LegacySniffMPEG4( - const sp &source, String8 *mimeType, float *confidence) { - uint8_t header[8]; - - ssize_t n = source->readAt(4, header, sizeof(header)); - if (n < (ssize_t)sizeof(header)) { - return false; - } - - if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8) - || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8) - || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8) - || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8) - || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8) - || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) { - *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.4; - - return true; - } - - return false; -} - -static bool isCompatibleBrand(uint32_t fourcc) { - static const uint32_t kCompatibleBrands[] = { - FOURCC('i', 's', 'o', 'm'), - FOURCC('i', 's', 'o', '2'), - FOURCC('a', 'v', 'c', '1'), - FOURCC('3', 'g', 'p', '4'), - FOURCC('m', 'p', '4', '1'), - FOURCC('m', 'p', '4', '2'), - - // Won't promise that the following file types can be played. - // Just give these file types a chance. - FOURCC('q', 't', ' ', ' '), // Apple's QuickTime - FOURCC('M', 'S', 'N', 'V'), // Sony's PSP - - FOURCC('3', 'g', '2', 'a'), // 3GPP2 - FOURCC('3', 'g', '2', 'b'), - }; - - for (size_t i = 0; - i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]); - ++i) { - if (kCompatibleBrands[i] == fourcc) { - return true; - } - } - - return false; -} - -#if 0 -// Attempt to actually parse the 'ftyp' atom and determine if a suitable -// compatible brand is present. -// Also try to identify where this file's metadata ends -// (end of the 'moov' atom) and report it to the caller as part of -// the metadata. -static bool BetterSniffMPEG4( - const sp &source, String8 *mimeType, float *confidence, - sp *meta) { - // We scan up to 128 bytes to identify this file as an MP4. - static const off64_t kMaxScanOffset = 128ll; - - off64_t offset = 0ll; - bool foundGoodFileType = false; - off64_t moovAtomEndOffset = -1ll; - bool done = false; - - while (!done && offset < kMaxScanOffset) { - uint32_t hdr[2]; - if (source->readAt(offset, hdr, 8) < 8) { - return false; - } - - uint64_t chunkSize = ntohl(hdr[0]); - uint32_t chunkType = ntohl(hdr[1]); - off64_t chunkDataOffset = offset + 8; - - if (chunkSize == 1) { - if (source->readAt(offset + 8, &chunkSize, 8) < 8) { - return false; - } - - chunkSize = ntoh64(chunkSize); - chunkDataOffset += 8; - - if (chunkSize < 16) { - // The smallest valid chunk is 16 bytes long in this case. - return false; - } - } else if (chunkSize < 8) { - // The smallest valid chunk is 8 bytes long. - return false; - } - - off64_t chunkDataSize = offset + chunkSize - chunkDataOffset; - - char chunkstring[5]; - MakeFourCCString(chunkType, chunkstring); - ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset); - switch (chunkType) { - case FOURCC('f', 't', 'y', 'p'): - { - if (chunkDataSize < 8) { - return false; - } - - uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; - for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { - if (i == 1) { - // Skip this index, it refers to the minorVersion, - // not a brand. - continue; - } - - uint32_t brand; - if (source->readAt( - chunkDataOffset + 4 * i, &brand, 4) < 4) { - return false; - } - - brand = ntohl(brand); - - if (isCompatibleBrand(brand)) { - foundGoodFileType = true; - break; - } - } - - if (!foundGoodFileType) { - return false; - } - - break; - } - - case FOURCC('m', 'o', 'o', 'v'): - { - moovAtomEndOffset = offset + chunkSize; - - done = true; - break; - } - - default: - break; - } - - offset += chunkSize; - } - - if (!foundGoodFileType) { - return false; - } - - *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.4f; - - if (moovAtomEndOffset >= 0) { - *meta = new AMessage; - (*meta)->setInt64("meta-data-size", moovAtomEndOffset); - - ALOGV("found metadata size: %lld", moovAtomEndOffset); - } - - return true; -} - -bool SniffMPEG4( - const sp &source, String8 *mimeType, float *confidence, - sp *meta) { - if (BetterSniffMPEG4(source, mimeType, confidence, meta)) { - return true; - } - - if (LegacySniffMPEG4(source, mimeType, confidence)) { - ALOGW("Identified supported mpeg4 through LegacySniffMPEG4."); - return true; - } - - return false; -} -#endif - } // namespace stagefright #undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp index faafe593b096..581a26472143 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp @@ -22,43 +22,4 @@ MediaSource::MediaSource() {} MediaSource::~MediaSource() {} -//////////////////////////////////////////////////////////////////////////////// - -MediaSource::ReadOptions::ReadOptions() { - reset(); -} - -void MediaSource::ReadOptions::reset() { - mOptions = 0; - mSeekTimeUs = 0; - mLatenessUs = 0; -} - -void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) { - mOptions |= kSeekTo_Option; - mSeekTimeUs = time_us; - mSeekMode = mode; -} - -void MediaSource::ReadOptions::clearSeekTo() { - mOptions &= ~kSeekTo_Option; - mSeekTimeUs = 0; - mSeekMode = SEEK_CLOSEST_SYNC; -} - -bool MediaSource::ReadOptions::getSeekTo( - int64_t *time_us, SeekMode *mode) const { - *time_us = mSeekTimeUs; - *mode = mSeekMode; - return (mOptions & kSeekTo_Option) != 0; -} - -void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) { - mLatenessUs = lateness_us; -} - -int64_t MediaSource::ReadOptions::getLateBy() const { - return mLatenessUs; -} - } // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h index 68030c0f4715..f729427f58a6 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h @@ -41,7 +41,6 @@ struct SidxEntry { class MPEG4Extractor : public MediaExtractor { public: - // Extractor assumes ownership of "source". MPEG4Extractor(const sp &source); virtual size_t countTracks(); @@ -149,16 +148,10 @@ private: void storeEditList(); - Track *findTrackByMimePrefix(const char *mimePrefix); - MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); }; -bool SniffMPEG4( - const sp &source, String8 *mimeType, float *confidence, - sp *); - } // namespace stagefright #endif // MPEG4_EXTRACTOR_H_ From 23f5557d82554bbcbac74d6c7a270f22ef99d517 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Tue, 1 Dec 2015 13:45:55 -0800 Subject: [PATCH 029/102] Bug 1210319 - p2. Minor intf clean-up, RIP trex - r=rillian Made some class interfaces a bit more "C++11"-ish, to protect against some possible issues. Also removed 'trex', which was only used by code removed in previous patch. --HG-- extra : commitid : FkTiMJ5sZLf --- .../include/media/stagefright/MediaSource.h | 4 +- .../media/libstagefright/MPEG4Extractor.cpp | 52 ++++--------------- .../libstagefright/include/MPEG4Extractor.h | 33 +++--------- 3 files changed, 18 insertions(+), 71 deletions(-) diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h index b9296d017144..913fca6d9db2 100644 --- a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h @@ -52,8 +52,8 @@ protected: virtual ~MediaSource(); private: - MediaSource(const MediaSource &); - MediaSource &operator=(const MediaSource &); + MediaSource(const MediaSource &) = delete; + MediaSource &operator=(const MediaSource &) = delete; }; } // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index 84ea78b4aa50..0890c4c12b30 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -50,9 +50,9 @@ public: uint32_t timeScale, const sp &sampleTable); - virtual sp getFormat(); + sp getFormat() override; - virtual nsTArray exportIndex(); + nsTArray exportIndex() override; protected: virtual ~MPEG4Source(); @@ -62,8 +62,8 @@ private: uint32_t mTimescale; sp mSampleTable; - MPEG4Source(const MPEG4Source &); - MPEG4Source &operator=(const MPEG4Source &); + MPEG4Source(const MPEG4Source &) = delete; + MPEG4Source &operator=(const MPEG4Source &) = delete; }; // This custom data source wraps an existing one and satisfies requests @@ -76,10 +76,10 @@ private: struct MPEG4DataSource : public DataSource { MPEG4DataSource(const sp &source); - virtual status_t initCheck() const; - virtual ssize_t readAt(off64_t offset, void *data, size_t size); - virtual status_t getSize(off64_t *size); - virtual uint32_t flags(); + status_t initCheck() const override; + ssize_t readAt(off64_t offset, void *data, size_t size) override; + status_t getSize(off64_t *size) override; + uint32_t flags() override; status_t setCachedRange(off64_t offset, size_t size); @@ -96,8 +96,8 @@ private: void clearCache(); - MPEG4DataSource(const MPEG4DataSource &); - MPEG4DataSource &operator=(const MPEG4DataSource &); + MPEG4DataSource(const MPEG4DataSource &) = delete; + MPEG4DataSource &operator=(const MPEG4DataSource &) = delete; }; MPEG4DataSource::MPEG4DataSource(const sp &source) @@ -1000,17 +1000,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'r', 'e', 'x'): - { - status_t err; - if ((err = parseTrackExtends(data_offset, chunk_data_size)) != OK) { - return err; - } - - *offset += chunk_size; - break; - } - case FOURCC('t', 'k', 'h', 'd'): { status_t err; @@ -2145,27 +2134,6 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { return OK; } -status_t MPEG4Extractor::parseTrackExtends( - off64_t data_offset, off64_t data_size) { - if (data_size != 24) { - return ERROR_MALFORMED; - } - uint8_t buffer[24]; - if (mDataSource->readAt(data_offset, buffer, 24) < 24) { - return ERROR_IO; - } - mTrackExtends.mVersion = buffer[0]; - mTrackExtends.mFlags[0] = buffer[1]; - mTrackExtends.mFlags[1] = buffer[2]; - mTrackExtends.mFlags[2] = buffer[3]; - mTrackExtends.mTrackId = U32_AT(&buffer[4]); - mTrackExtends.mDefaultSampleDescriptionIndex = U32_AT(&buffer[8]); - mTrackExtends.mDefaultSampleDuration = U32_AT(&buffer[12]); - mTrackExtends.mDefaultSampleSize = U32_AT(&buffer[16]); - mTrackExtends.mDefaultSampleFlags = U32_AT(&buffer[20]); - return OK; -} - status_t MPEG4Extractor::parseTrackHeader( off64_t data_offset, off64_t data_size) { if (data_size < 4) { diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h index f729427f58a6..86579d39b276 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h @@ -43,33 +43,15 @@ class MPEG4Extractor : public MediaExtractor { public: MPEG4Extractor(const sp &source); - virtual size_t countTracks(); - virtual sp getTrack(size_t index); - virtual sp getTrackMetaData(size_t index, uint32_t flags); + size_t countTracks() override; + sp getTrack(size_t index) override; + sp getTrackMetaData(size_t index, uint32_t flags) override; - virtual sp getMetaData(); - virtual uint32_t flags() const; + sp getMetaData() override; + uint32_t flags() const override; // for DRM - virtual char* getDrmTrackInfo(size_t trackID, int *len); - - struct TrackExtends { - TrackExtends(): mVersion(0), mTrackId(0), - mDefaultSampleDescriptionIndex(0), mDefaultSampleDuration(0), - mDefaultSampleSize(0), mDefaultSampleFlags(0) - { - mFlags[0] = 0; - mFlags[1] = 0; - mFlags[2] = 0; - } - uint8_t mVersion; - uint8_t mFlags[3]; - uint32_t mTrackId; - uint32_t mDefaultSampleDescriptionIndex; - uint32_t mDefaultSampleDuration; - uint32_t mDefaultSampleSize; - uint32_t mDefaultSampleFlags; - }; + char* getDrmTrackInfo(size_t trackID, int *len) override; protected: virtual ~MPEG4Extractor(); @@ -135,13 +117,10 @@ private: SINF *mFirstSINF; bool mIsDrm; - TrackExtends mTrackExtends; uint32_t mDrmScheme; status_t parseDrmSINF(off64_t *offset, off64_t data_offset); - status_t parseTrackExtends(off64_t data_offset, off64_t data_size); - status_t parseTrackHeader(off64_t data_offset, off64_t data_size); status_t parseSegmentIndex(off64_t data_offset, size_t data_size); From a9a0ca3af0601192265af6e092da9e262216dd66 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Tue, 1 Dec 2015 13:46:07 -0800 Subject: [PATCH 030/102] Bug 1210319 - p3. Lowered useless log level - r=jya --HG-- extra : commitid : 3l4NCSLhsWC --- .../frameworks/av/media/libstagefright/SampleTable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp index f43b6691fcdb..9abc18618808 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp @@ -375,7 +375,7 @@ status_t SampleTable::setTimeToSampleParams( status_t SampleTable::setCompositionTimeToSampleParams( off64_t data_offset, size_t data_size) { - ALOGI("There are reordered frames present."); + ALOGV("There are reordered frames present."); if (mCompositionTimeDeltaEntries != NULL || data_size < 8) { return ERROR_MALFORMED; From 66199890c46445e968c377224fe3172834c9de93 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 30 Nov 2015 08:54:11 -0600 Subject: [PATCH 031/102] Bug 1218029 - Adds IncrementalStreamLoader interface stubs. r=djvj --HG-- extra : commitid : J0UubFG9gvz --- dom/base/nsScriptLoader.cpp | 20 +-- dom/base/nsScriptLoader.h | 8 +- dom/security/SRICheck.cpp | 4 +- dom/security/SRICheck.h | 4 +- dom/xul/XULDocument.h | 1 + js/xpconnect/loader/mozJSSubScriptLoader.cpp | 24 +-- netwerk/base/moz.build | 2 + netwerk/base/nsIIncrementalStreamLoader.idl | 73 +++++++++ netwerk/base/nsIncrementalStreamLoader.cpp | 153 +++++++++++++++++++ netwerk/base/nsIncrementalStreamLoader.h | 51 +++++++ netwerk/base/nsNetUtil.cpp | 18 +++ netwerk/base/nsNetUtil.h | 5 + netwerk/base/nsNetUtil.inl | 1 + netwerk/build/nsNetCID.h | 12 ++ netwerk/build/nsNetModule.cpp | 4 + 15 files changed, 350 insertions(+), 30 deletions(-) create mode 100644 netwerk/base/nsIIncrementalStreamLoader.idl create mode 100644 netwerk/base/nsIncrementalStreamLoader.cpp create mode 100644 netwerk/base/nsIncrementalStreamLoader.h diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index d480b4f1b239..e8a5699a16ac 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -157,10 +157,10 @@ nsScriptLoader::~nsScriptLoader() // subtree in the meantime and therefore aren't actually going away. for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) { mPendingChildLoaders[j]->RemoveExecuteBlocker(); - } + } } -NS_IMPL_ISUPPORTS(nsScriptLoader, nsIStreamLoaderObserver) +NS_IMPL_ISUPPORTS(nsScriptLoader, nsIIncrementalStreamLoaderObserver) // Helper method for checking if the script element is an event-handler // This means that it has both a for-attribute and a event-attribute. @@ -269,7 +269,7 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument, return NS_OK; } -class ContextMediator : public nsIStreamLoaderObserver +class ContextMediator : public nsIIncrementalStreamLoaderObserver { public: explicit ContextMediator(nsScriptLoader *aScriptLoader, nsISupports *aContext) @@ -277,7 +277,7 @@ public: , mContext(aContext) {} NS_DECL_ISUPPORTS - NS_DECL_NSISTREAMLOADEROBSERVER + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER private: virtual ~ContextMediator() {} @@ -285,10 +285,10 @@ private: nsCOMPtr mContext; }; -NS_IMPL_ISUPPORTS(ContextMediator, nsIStreamLoaderObserver) +NS_IMPL_ISUPPORTS(ContextMediator, nsIIncrementalStreamLoaderObserver) NS_IMETHODIMP -ContextMediator::OnStreamComplete(nsIStreamLoader* aLoader, +ContextMediator::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, uint32_t aStringLen, @@ -385,8 +385,8 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, RefPtr mediator = new ContextMediator(this, aRequest); - nsCOMPtr loader; - rv = NS_NewStreamLoader(getter_AddRefs(loader), mediator); + nsCOMPtr loader; + rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), mediator); NS_ENSURE_SUCCESS(rv, rv); return channel->AsyncOpen2(loader); @@ -1439,7 +1439,7 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, } NS_IMETHODIMP -nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, +nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, uint32_t aStringLen, @@ -1535,7 +1535,7 @@ nsScriptLoader::NumberOfProcessors() nsresult nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, - nsIStreamLoader* aLoader, + nsIIncrementalStreamLoader* aLoader, nsresult aStatus, uint32_t aStringLen, const uint8_t* aString) diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index 18ef6bc12f44..b3f0afb595ab 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -17,7 +17,7 @@ #include "nsTArray.h" #include "nsAutoPtr.h" #include "nsIDocument.h" -#include "nsIStreamLoader.h" +#include "nsIIncrementalStreamLoader.h" #include "mozilla/CORSMode.h" #include "mozilla/dom/SRIMetadata.h" #include "mozilla/LinkedList.h" @@ -195,7 +195,7 @@ public: // Script loader implementation ////////////////////////////////////////////////////////////// -class nsScriptLoader final : public nsIStreamLoaderObserver +class nsScriptLoader final : public nsIIncrementalStreamLoaderObserver { class MOZ_STACK_CLASS AutoCurrentScriptUpdater { @@ -223,7 +223,7 @@ public: explicit nsScriptLoader(nsIDocument* aDocument); NS_DECL_ISUPPORTS - NS_DECL_NSISTREAMLOADEROBSERVER + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER /** * The loader maintains a weak reference to the document with @@ -489,7 +489,7 @@ private: uint32_t NumberOfProcessors(); nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, - nsIStreamLoader* aLoader, + nsIIncrementalStreamLoader* aLoader, nsresult aStatus, uint32_t aStringLen, const uint8_t* aString); diff --git a/dom/security/SRICheck.cpp b/dom/security/SRICheck.cpp index 32a50d99d6e3..471669db0f5f 100644 --- a/dom/security/SRICheck.cpp +++ b/dom/security/SRICheck.cpp @@ -16,7 +16,7 @@ #include "nsIProtocolHandler.h" #include "nsIScriptError.h" #include "nsIScriptSecurityManager.h" -#include "nsIStreamLoader.h" +#include "nsIIncrementalStreamLoader.h" #include "nsIUnicharStreamLoader.h" #include "nsIURI.h" #include "nsNetUtil.h" @@ -317,7 +317,7 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, /* static */ nsresult SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, - nsIStreamLoader* aLoader, + nsIIncrementalStreamLoader* aLoader, const CORSMode aCORSMode, uint32_t aStringLen, const uint8_t* aString, diff --git a/dom/security/SRICheck.h b/dom/security/SRICheck.h index c6ac2e6b0b42..0aecba50fb70 100644 --- a/dom/security/SRICheck.h +++ b/dom/security/SRICheck.h @@ -12,7 +12,7 @@ #include "SRIMetadata.h" class nsIDocument; -class nsIStreamLoader; +class nsIIncrementalStreamLoader; class nsIUnicharStreamLoader; namespace mozilla { @@ -47,7 +47,7 @@ public: * must prevent the resource from loading. */ static nsresult VerifyIntegrity(const SRIMetadata& aMetadata, - nsIStreamLoader* aLoader, + nsIIncrementalStreamLoader* aLoader, const CORSMode aCORSMode, uint32_t aStringLen, const uint8_t* aString, diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h index 1246d6bb7402..397057b29730 100644 --- a/dom/xul/XULDocument.h +++ b/dom/xul/XULDocument.h @@ -21,6 +21,7 @@ #include "nsIXULDocument.h" #include "nsScriptLoader.h" #include "nsIStreamListener.h" +#include "nsIStreamLoader.h" #include "nsICSSLoaderObserver.h" #include "nsIXULStore.h" diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index 99cbb644e559..a5a255d56cd5 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -239,11 +239,11 @@ EvalScript(JSContext* cx, return NS_OK; } -class AsyncScriptLoader : public nsIStreamLoaderObserver +class AsyncScriptLoader : public nsIIncrementalStreamLoaderObserver { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_NSISTREAMLOADEROBSERVER + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader) @@ -277,7 +277,7 @@ private: NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader) - NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver) + NS_INTERFACE_MAP_ENTRY(nsIIncrementalStreamLoaderObserver) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader) @@ -326,7 +326,7 @@ class MOZ_STACK_CLASS AutoRejectPromise }; NS_IMETHODIMP -AsyncScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, +AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, uint32_t aLength, @@ -431,8 +431,8 @@ mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri, JSObject* targetObjArg, cache, promise); - nsCOMPtr loader; - rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver); + nsCOMPtr loader; + rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr listener = loader.get(); @@ -673,11 +673,11 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url, * Let us compile scripts from a URI off the main thread. */ -class ScriptPrecompiler : public nsIStreamLoaderObserver +class ScriptPrecompiler : public nsIIncrementalStreamLoaderObserver { public: NS_DECL_ISUPPORTS - NS_DECL_NSISTREAMLOADEROBSERVER + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER ScriptPrecompiler(nsIObserver* aObserver, nsIPrincipal* aPrincipal, @@ -709,7 +709,7 @@ private: size_t mScriptLength; }; -NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIStreamLoaderObserver); +NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIIncrementalStreamLoaderObserver); class NotifyPrecompilationCompleteRunnable : public nsRunnable { @@ -770,7 +770,7 @@ NotifyPrecompilationCompleteRunnable::Run(void) } NS_IMETHODIMP -ScriptPrecompiler::OnStreamComplete(nsIStreamLoader* aLoader, +ScriptPrecompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, uint32_t aLength, @@ -876,8 +876,8 @@ mozJSSubScriptLoader::PrecompileScript(nsIURI* aURI, RefPtr loadObserver = new ScriptPrecompiler(aObserver, aPrincipal, channel); - nsCOMPtr loader; - rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver); + nsCOMPtr loader; + rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr listener = loader.get(); diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 719bdf7a0691..3444092bd2f8 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -48,6 +48,7 @@ XPIDL_SOURCES += [ 'nsIForcePendingChannel.idl', 'nsIHttpPushListener.idl', 'nsIIncrementalDownload.idl', + 'nsIIncrementalStreamLoader.idl', 'nsIInputStreamChannel.idl', 'nsIInputStreamPump.idl', 'nsIIOService.idl', @@ -212,6 +213,7 @@ UNIFIED_SOURCES += [ 'nsDownloader.cpp', 'nsFileStreams.cpp', 'nsIncrementalDownload.cpp', + 'nsIncrementalStreamLoader.cpp', 'nsInputStreamChannel.cpp', 'nsInputStreamPump.cpp', 'nsIOService.cpp', diff --git a/netwerk/base/nsIIncrementalStreamLoader.idl b/netwerk/base/nsIIncrementalStreamLoader.idl new file mode 100644 index 000000000000..1e4af9a99d60 --- /dev/null +++ b/netwerk/base/nsIIncrementalStreamLoader.idl @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIStreamListener.idl" + +interface nsIRequest; +interface nsIIncrementalStreamLoader; + +[scriptable, uuid(2143eaad-674e-4613-87f8-359d4c40f590)] +interface nsIIncrementalStreamLoaderObserver : nsISupports +{ + /** + * Called when the entire stream has been loaded. + * + * @param loader the stream loader that loaded the stream. + * @param ctxt the context parameter of the underlying channel + * @param status the status of the underlying channel + * @param resultLength the length of the data loaded + * @param result the data + * + * This method will always be called asynchronously by the + * nsIIncrementalStreamLoader involved, on the thread that called the + * loader's init() method. + * + * If the observer wants to take over responsibility for the + * data buffer (result), it returns NS_SUCCESS_ADOPTED_DATA + * in place of NS_OK as its success code. The loader will then + * "forget" about the data and not free() it after + * onStreamComplete() returns; observer must call free() + * when the data is no longer required. + */ + void onStreamComplete(in nsIIncrementalStreamLoader loader, + in nsISupports ctxt, + in nsresult status, + in unsigned long resultLength, + [const,array,size_is(resultLength)] in octet result); +}; + +/** + * Asynchronously loads a channel into a memory buffer. + * + * To use this interface, first call init() with a nsIIncrementalStreamLoaderObserver + * that will be notified when the data has been loaded. Then call asyncOpen() + * on the channel with the nsIIncrementalStreamLoader as the listener. The context + * argument in the asyncOpen() call will be passed to the onStreamComplete() + * callback. + * + * XXX define behaviour for sizes >4 GB + */ +[scriptable, uuid(a023b060-ba23-431a-b449-2dd63e220554)] +interface nsIIncrementalStreamLoader : nsIStreamListener +{ + /** + * Initialize this stream loader, and start loading the data. + * + * @param aObserver + * An observer that will be notified when the data is complete. + */ + void init(in nsIIncrementalStreamLoaderObserver aObserver); + + /** + * Gets the number of bytes read so far. + */ + readonly attribute unsigned long numBytesRead; + + /** + * Gets the request that loaded this file. + * null after the request has finished loading. + */ + readonly attribute nsIRequest request; +}; diff --git a/netwerk/base/nsIncrementalStreamLoader.cpp b/netwerk/base/nsIncrementalStreamLoader.cpp new file mode 100644 index 000000000000..49726625479d --- /dev/null +++ b/netwerk/base/nsIncrementalStreamLoader.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIncrementalStreamLoader.h" +#include "nsIInputStream.h" +#include "nsIChannel.h" +#include "nsError.h" +#include "GeckoProfiler.h" + +#include + +nsIncrementalStreamLoader::nsIncrementalStreamLoader() + : mData() +{ +} + +nsIncrementalStreamLoader::~nsIncrementalStreamLoader() +{ +} + +NS_IMETHODIMP +nsIncrementalStreamLoader::Init(nsIIncrementalStreamLoaderObserver* observer) +{ + NS_ENSURE_ARG_POINTER(observer); + mObserver = observer; + return NS_OK; +} + +nsresult +nsIncrementalStreamLoader::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + if (aOuter) return NS_ERROR_NO_AGGREGATION; + + nsIncrementalStreamLoader* it = new nsIncrementalStreamLoader(); + if (it == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(it); + nsresult rv = it->QueryInterface(aIID, aResult); + NS_RELEASE(it); + return rv; +} + +NS_IMPL_ISUPPORTS(nsIncrementalStreamLoader, nsIIncrementalStreamLoader, + nsIRequestObserver, nsIStreamListener, + nsIThreadRetargetableStreamListener) + +NS_IMETHODIMP +nsIncrementalStreamLoader::GetNumBytesRead(uint32_t* aNumBytes) +{ + *aNumBytes = mData.length(); + return NS_OK; +} + +/* readonly attribute nsIRequest request; */ +NS_IMETHODIMP +nsIncrementalStreamLoader::GetRequest(nsIRequest **aRequest) +{ + NS_IF_ADDREF(*aRequest = mRequest); + return NS_OK; +} + +NS_IMETHODIMP +nsIncrementalStreamLoader::OnStartRequest(nsIRequest* request, nsISupports *ctxt) +{ + nsCOMPtr chan( do_QueryInterface(request) ); + if (chan) { + int64_t contentLength = -1; + chan->GetContentLength(&contentLength); + if (contentLength >= 0) { + if (uint64_t(contentLength) > std::numeric_limits::max()) { + // Too big to fit into size_t, so let's bail. + return NS_ERROR_OUT_OF_MEMORY; + } + // preallocate buffer + if (!mData.initCapacity(contentLength)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + mContext = ctxt; + return NS_OK; +} + +NS_IMETHODIMP +nsIncrementalStreamLoader::OnStopRequest(nsIRequest* request, nsISupports *ctxt, + nsresult aStatus) +{ + PROFILER_LABEL("nsIncrementalStreamLoader", "OnStopRequest", + js::ProfileEntry::Category::NETWORK); + + if (mObserver) { + // provide nsIIncrementalStreamLoader::request during call to OnStreamComplete + mRequest = request; + size_t length = mData.length(); + uint8_t* elems = mData.extractRawBuffer(); + nsresult rv = mObserver->OnStreamComplete(this, mContext, aStatus, + length, elems); + if (rv != NS_SUCCESS_ADOPTED_DATA) { + // The observer didn't take ownership of the extracted data buffer, so + // put it back into mData. + mData.replaceRawBuffer(elems, length); + } + // done.. cleanup + ReleaseData(); + mRequest = 0; + mObserver = 0; + mContext = 0; + } + return NS_OK; +} + +NS_METHOD +nsIncrementalStreamLoader::WriteSegmentFun(nsIInputStream *inStr, + void *closure, + const char *fromSegment, + uint32_t toOffset, + uint32_t count, + uint32_t *writeCount) +{ + nsIncrementalStreamLoader *self = (nsIncrementalStreamLoader *) closure; + + if (!self->mData.append(fromSegment, count)) { + self->mData.clearAndFree(); + return NS_ERROR_OUT_OF_MEMORY; + } + + *writeCount = count; + + return NS_OK; +} + +NS_IMETHODIMP +nsIncrementalStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, + nsIInputStream *inStr, + uint64_t sourceOffset, uint32_t count) +{ + uint32_t countRead; + return inStr->ReadSegments(WriteSegmentFun, this, count, &countRead); +} + +void +nsIncrementalStreamLoader::ReleaseData() +{ + mData.clearAndFree(); +} + +NS_IMETHODIMP +nsIncrementalStreamLoader::CheckListenerChain() +{ + return NS_OK; +} diff --git a/netwerk/base/nsIncrementalStreamLoader.h b/netwerk/base/nsIncrementalStreamLoader.h new file mode 100644 index 000000000000..40fa0721daef --- /dev/null +++ b/netwerk/base/nsIncrementalStreamLoader.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsIncrementalStreamLoader_h__ +#define nsIncrementalStreamLoader_h__ + +#include "nsIThreadRetargetableStreamListener.h" +#include "nsIIncrementalStreamLoader.h" +#include "nsCOMPtr.h" +#include "mozilla/Attributes.h" +#include "mozilla/Vector.h" + +class nsIRequest; + +class nsIncrementalStreamLoader final : public nsIIncrementalStreamLoader + , public nsIThreadRetargetableStreamListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIINCREMENTALSTREAMLOADER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER + + nsIncrementalStreamLoader(); + + static nsresult + Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + +protected: + ~nsIncrementalStreamLoader(); + + static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *, + uint32_t, uint32_t, uint32_t *); + + // Utility method to free mData, if present, and update other state to + // reflect that no data has been allocated. + void ReleaseData(); + + nsCOMPtr mObserver; + nsCOMPtr mContext; // the observer's context + nsCOMPtr mRequest; + + // Buffer to accumulate incoming data. We preallocate if contentSize is + // available. + mozilla::Vector mData; +}; + +#endif // nsIncrementalStreamLoader_h__ diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 4181199dcb71..42e7795358f5 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -45,6 +45,7 @@ #include "nsISocketProviderService.h" #include "nsIStandardURL.h" #include "nsIStreamLoader.h" +#include "nsIIncrementalStreamLoader.h" #include "nsIStreamTransportService.h" #include "nsStringStream.h" #include "nsISyncStreamListener.h" @@ -580,6 +581,23 @@ NS_NewDownloader(nsIStreamListener **result, return rv; } +nsresult +NS_NewIncrementalStreamLoader(nsIIncrementalStreamLoader **result, + nsIIncrementalStreamLoaderObserver *observer) +{ + nsresult rv; + nsCOMPtr loader = + do_CreateInstance(NS_INCREMENTALSTREAMLOADER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = loader->Init(observer); + if (NS_SUCCEEDED(rv)) { + *result = nullptr; + loader.swap(*result); + } + } + return rv; +} + nsresult NS_NewStreamLoaderInternal(nsIStreamLoader **outStream, nsIURI *aUri, diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 583a567cf7b5..232534c0a505 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -42,6 +42,8 @@ class nsIRequestObserver; class nsIStreamListener; class nsIStreamLoader; class nsIStreamLoaderObserver; +class nsIIncrementalStreamLoader; +class nsIIncrementalStreamLoaderObserver; class nsIUnicharStreamLoader; class nsIUnicharStreamLoaderObserver; @@ -369,6 +371,9 @@ nsresult NS_NewStreamLoader(nsIStreamLoader **result, nsIStreamLoaderObserver *observer, nsIRequestObserver *requestObserver = nullptr); +nsresult NS_NewIncrementalStreamLoader(nsIIncrementalStreamLoader **result, + nsIIncrementalStreamLoaderObserver *observer); + nsresult NS_NewStreamLoaderInternal(nsIStreamLoader **outStream, nsIURI *aUri, nsIStreamLoaderObserver *aObserver, diff --git a/netwerk/base/nsNetUtil.inl b/netwerk/base/nsNetUtil.inl index 550327bb9340..65462e676b2c 100644 --- a/netwerk/base/nsNetUtil.inl +++ b/netwerk/base/nsNetUtil.inl @@ -23,6 +23,7 @@ #include "nsIProtocolHandler.h" #include "nsIStandardURL.h" #include "nsIStreamLoader.h" +#include "nsIIncrementalStreamLoader.h" #include "nsIURI.h" #include "nsIURIWithPrincipal.h" #include "nsIWritablePropertyBag2.h" diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index 43047ebfde6d..31d03726c0cf 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -226,6 +226,18 @@ { 0xa1, 0xa5, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44 } \ } +// component implementing nsIStreamLoader. +#define NS_INCREMENTALSTREAMLOADER_CONTRACTID \ + "@mozilla.org/network/incremental-stream-loader;1" +#define NS_INCREMENTALSTREAMLOADER_CID \ +{ /* 5d6352a3-b9c3-4fa3-87aa-b2a3c6e5a501 */ \ + 0x5d6352a3, \ + 0xb9c3, \ + 0x4fa3, \ + {0x87, 0xaa, 0xb2, 0xa3, 0xc6, 0xe5, 0xa5, 0x01} \ +} + + // component implementing nsIUnicharStreamLoader. #define NS_UNICHARSTREAMLOADER_CONTRACTID \ "@mozilla.org/network/unichar-stream-loader;1" diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 425b094e7f10..b665602dfa2d 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -21,6 +21,7 @@ #include "nsSimpleNestedURI.h" #include "nsLoadGroup.h" #include "nsStreamLoader.h" +#include "nsIncrementalStreamLoader.h" #include "nsUnicharStreamLoader.h" #include "nsFileStreams.h" #include "nsBufferedStreams.h" @@ -707,6 +708,7 @@ NS_DEFINE_NAMED_CID(NS_ASYNCSTREAMCOPIER_CID); NS_DEFINE_NAMED_CID(NS_INPUTSTREAMPUMP_CID); NS_DEFINE_NAMED_CID(NS_INPUTSTREAMCHANNEL_CID); NS_DEFINE_NAMED_CID(NS_STREAMLOADER_CID); +NS_DEFINE_NAMED_CID(NS_INCREMENTALSTREAMLOADER_CID); NS_DEFINE_NAMED_CID(NS_UNICHARSTREAMLOADER_CID); NS_DEFINE_NAMED_CID(NS_DOWNLOADER_CID); NS_DEFINE_NAMED_CID(NS_BACKGROUNDFILESAVEROUTPUTSTREAM_CID); @@ -854,6 +856,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_INPUTSTREAMPUMP_CID, false, nullptr, nsInputStreamPumpConstructor }, { &kNS_INPUTSTREAMCHANNEL_CID, false, nullptr, nsInputStreamChannelConstructor }, { &kNS_STREAMLOADER_CID, false, nullptr, nsStreamLoader::Create }, + { &kNS_INCREMENTALSTREAMLOADER_CID, false, nullptr, nsIncrementalStreamLoader::Create }, { &kNS_UNICHARSTREAMLOADER_CID, false, nullptr, nsUnicharStreamLoader::Create }, { &kNS_DOWNLOADER_CID, false, nullptr, nsDownloaderConstructor }, { &kNS_BACKGROUNDFILESAVEROUTPUTSTREAM_CID, false, nullptr, @@ -1007,6 +1010,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = { { NS_INPUTSTREAMPUMP_CONTRACTID, &kNS_INPUTSTREAMPUMP_CID }, { NS_INPUTSTREAMCHANNEL_CONTRACTID, &kNS_INPUTSTREAMCHANNEL_CID }, { NS_STREAMLOADER_CONTRACTID, &kNS_STREAMLOADER_CID }, + { NS_INCREMENTALSTREAMLOADER_CONTRACTID, &kNS_INCREMENTALSTREAMLOADER_CID }, { NS_UNICHARSTREAMLOADER_CONTRACTID, &kNS_UNICHARSTREAMLOADER_CID }, { NS_DOWNLOADER_CONTRACTID, &kNS_DOWNLOADER_CID }, { NS_BACKGROUNDFILESAVEROUTPUTSTREAM_CONTRACTID, &kNS_BACKGROUNDFILESAVEROUTPUTSTREAM_CID }, From b984c50fec2115ca329ae74eaabeb3fbd997af64 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 1 Dec 2015 08:00:58 -0600 Subject: [PATCH 032/102] Bug 1218029 - Adds ScriptLoadHandler and implements OnIncrementalData callback. r=djvj --HG-- extra : commitid : 7TyrlgQ2f2Z --- dom/base/nsScriptLoader.cpp | 77 +++++++++--------- dom/base/nsScriptLoader.h | 30 ++++++- js/xpconnect/loader/mozJSSubScriptLoader.cpp | 20 +++++ netwerk/base/nsIIncrementalStreamLoader.idl | 29 ++++++- netwerk/base/nsIncrementalStreamLoader.cpp | 73 +++++++++++++++-- netwerk/base/nsIncrementalStreamLoader.h | 3 + netwerk/test/unit/test_bug1218029.js | 82 ++++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 1 + 8 files changed, 271 insertions(+), 44 deletions(-) create mode 100644 netwerk/test/unit/test_bug1218029.js diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index e8a5699a16ac..7162073ee4e7 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -160,7 +160,7 @@ nsScriptLoader::~nsScriptLoader() } } -NS_IMPL_ISUPPORTS(nsScriptLoader, nsIIncrementalStreamLoaderObserver) +NS_IMPL_ISUPPORTS(nsScriptLoader, nsISupports) // Helper method for checking if the script element is an event-handler // This means that it has both a for-attribute and a event-attribute. @@ -269,37 +269,6 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument, return NS_OK; } -class ContextMediator : public nsIIncrementalStreamLoaderObserver -{ -public: - explicit ContextMediator(nsScriptLoader *aScriptLoader, nsISupports *aContext) - : mScriptLoader(aScriptLoader) - , mContext(aContext) {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER - -private: - virtual ~ContextMediator() {} - RefPtr mScriptLoader; - nsCOMPtr mContext; -}; - -NS_IMPL_ISUPPORTS(ContextMediator, nsIIncrementalStreamLoaderObserver) - -NS_IMETHODIMP -ContextMediator::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) -{ - // pass arguments through except for the aContext, - // we have to mediate and use mContext instead. - return mScriptLoader->OnStreamComplete(aLoader, mContext, aStatus, - aStringLen, aString); -} - nsresult nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, bool aScriptFromHead) @@ -383,10 +352,10 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, timedChannel->SetInitiatorType(NS_LITERAL_STRING("script")); } - RefPtr mediator = new ContextMediator(this, aRequest); + RefPtr handler = new nsScriptLoadHandler(this, aRequest); nsCOMPtr loader; - rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), mediator); + rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler); NS_ENSURE_SUCCESS(rv, rv); return channel->AsyncOpen2(loader); @@ -1438,7 +1407,7 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, return rv; } -NS_IMETHODIMP +nsresult nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, @@ -1739,3 +1708,41 @@ nsScriptLoader::MaybeRemovedDeferRequests() } return false; } + +////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////// + +nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader, + nsScriptLoadRequest *aRequest) + : mScriptLoader(aScriptLoader), + mRequest(aRequest) +{} + +nsScriptLoadHandler::~nsScriptLoadHandler() +{} + +NS_IMPL_ISUPPORTS(nsScriptLoadHandler, nsIIncrementalStreamLoaderObserver) + +NS_IMETHODIMP +nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + uint32_t aDataLength, + const uint8_t* aData, + uint32_t *aConsumedLength) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + uint32_t aStringLen, + const uint8_t* aString) +{ + // pass arguments through except for the aContext, + // we have to mediate and use mRequest instead. + return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, + aStringLen, aString); +} diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index b3f0afb595ab..8261c49cb0cd 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -195,7 +195,7 @@ public: // Script loader implementation ////////////////////////////////////////////////////////////// -class nsScriptLoader final : public nsIIncrementalStreamLoaderObserver +class nsScriptLoader final : public nsISupports { class MOZ_STACK_CLASS AutoCurrentScriptUpdater { @@ -223,7 +223,6 @@ public: explicit nsScriptLoader(nsIDocument* aDocument); NS_DECL_ISUPPORTS - NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER /** * The loader maintains a weak reference to the document with @@ -342,6 +341,17 @@ public: nsIDocument* aDocument, char16_t*& aBufOut, size_t& aLengthOut); + /** + * Handle the completion of a stream. This is called by the + * nsScriptLoadHandler object which observes the IncrementalStreamLoader + * loading the script. + */ + nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + uint32_t aStringLen, + const uint8_t* aString); + /** * Processes any pending requests that are ready for processing. */ @@ -538,6 +548,22 @@ private: bool mBlockingDOMContentLoaded; }; +class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver +{ +public: + explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader, + nsScriptLoadRequest *aRequest); + + NS_DECL_ISUPPORTS + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER + +private: + virtual ~nsScriptLoadHandler(); + + RefPtr mScriptLoader; + RefPtr mRequest; +}; + class nsAutoScriptLoaderDisabler { public: diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index a5a255d56cd5..67b27dcac0e1 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -325,6 +325,16 @@ class MOZ_STACK_CLASS AutoRejectPromise nsCOMPtr mGlobalObject; }; +NS_IMETHODIMP +AsyncScriptLoader::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + uint32_t aDataLength, + const uint8_t* aData, + uint32_t *aConsumedData) +{ + return NS_OK; +} + NS_IMETHODIMP AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, @@ -769,6 +779,16 @@ NotifyPrecompilationCompleteRunnable::Run(void) return NS_OK; } +NS_IMETHODIMP +ScriptPrecompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + uint32_t aDataLength, + const uint8_t* aData, + uint32_t *aConsumedData) +{ + return NS_OK; +} + NS_IMETHODIMP ScriptPrecompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, diff --git a/netwerk/base/nsIIncrementalStreamLoader.idl b/netwerk/base/nsIIncrementalStreamLoader.idl index 1e4af9a99d60..60aa9cfef55c 100644 --- a/netwerk/base/nsIIncrementalStreamLoader.idl +++ b/netwerk/base/nsIIncrementalStreamLoader.idl @@ -8,9 +8,36 @@ interface nsIRequest; interface nsIIncrementalStreamLoader; -[scriptable, uuid(2143eaad-674e-4613-87f8-359d4c40f590)] +[scriptable, uuid(07c3d2cc-5454-4618-9f4f-cd93de9504a4)] interface nsIIncrementalStreamLoaderObserver : nsISupports { + /** + * Called when new data has arrived on the stream. + * + * @param loader the stream loader that loaded the stream. + * @param ctxt the context parameter of the underlying channel + * @param dataLength the length of the new data received + * @param data the contents of the new data received. + * + * This method will always be called asynchronously by the + * nsIIncrementalStreamLoader involved, on the thread that called the + * loader's init() method. + * + * If the observer wants to not accumulate all or portional of the data in + * the internal buffer, the consumedLength shall be set to the value of + * the dataLength or less. By default the consumedLength value is assumed 0. + * The data and dataLength reflect the non-consumed data and will be + * accumulated if consumedLength is not set. + * + * In comparison with onStreamComplete(), the data buffer cannot be + * adopted if this method returns NS_SUCCESS_ADOPTED_DATA. + */ + void onIncrementalData(in nsIIncrementalStreamLoader loader, + in nsISupports ctxt, + in unsigned long dataLength, + [const,array,size_is(dataLength)] in octet data, + inout unsigned long consumedLength); + /** * Called when the entire stream has been loaded. * diff --git a/netwerk/base/nsIncrementalStreamLoader.cpp b/netwerk/base/nsIncrementalStreamLoader.cpp index 49726625479d..4f4e144b8f0e 100644 --- a/netwerk/base/nsIncrementalStreamLoader.cpp +++ b/netwerk/base/nsIncrementalStreamLoader.cpp @@ -12,7 +12,7 @@ #include nsIncrementalStreamLoader::nsIncrementalStreamLoader() - : mData() + : mData(), mBytesConsumed(0) { } @@ -49,7 +49,7 @@ NS_IMPL_ISUPPORTS(nsIncrementalStreamLoader, nsIIncrementalStreamLoader, NS_IMETHODIMP nsIncrementalStreamLoader::GetNumBytesRead(uint32_t* aNumBytes) { - *aNumBytes = mData.length(); + *aNumBytes = mBytesConsumed + mData.length(); return NS_OK; } @@ -121,11 +121,66 @@ nsIncrementalStreamLoader::WriteSegmentFun(nsIInputStream *inStr, { nsIncrementalStreamLoader *self = (nsIncrementalStreamLoader *) closure; - if (!self->mData.append(fromSegment, count)) { - self->mData.clearAndFree(); - return NS_ERROR_OUT_OF_MEMORY; + const uint8_t *data = reinterpret_cast(fromSegment); + uint32_t consumedCount = 0; + nsresult rv; + if (self->mData.empty()) { + // Shortcut when observer wants to keep the listener's buffer empty. + rv = self->mObserver->OnIncrementalData(self, self->mContext, + count, data, &consumedCount); + + if (rv != NS_OK) { + return rv; + } + + if (consumedCount > count) { + return NS_ERROR_INVALID_ARG; + } + + if (consumedCount < count) { + if (!self->mData.append(fromSegment + consumedCount, + count - consumedCount)) { + self->mData.clearAndFree(); + return NS_ERROR_OUT_OF_MEMORY; + } + } + } else { + // We have some non-consumed data from previous OnIncrementalData call, + // appending new data and reporting combined data. + if (!self->mData.append(fromSegment, count)) { + self->mData.clearAndFree(); + return NS_ERROR_OUT_OF_MEMORY; + } + size_t length = self->mData.length(); + uint32_t reportCount = length > UINT32_MAX ? UINT32_MAX : (uint32_t)length; + uint8_t* elems = self->mData.extractRawBuffer(); + + rv = self->mObserver->OnIncrementalData(self, self->mContext, + reportCount, elems, &consumedCount); + + // We still own elems, freeing its memory when exiting scope. + if (rv != NS_OK) { + free(elems); + return rv; + } + + if (consumedCount > reportCount) { + free(elems); + return NS_ERROR_INVALID_ARG; + } + + if (consumedCount == length) { + free(elems); // good case -- fully consumed data + } else { + // Adopting elems back (at least its portion). + self->mData.replaceRawBuffer(elems, length); + if (consumedCount > 0) { + self->mData.erase(self->mData.begin() + consumedCount); + } + } } + self->mBytesConsumed += consumedCount; *writeCount = count; return NS_OK; @@ -136,8 +191,14 @@ nsIncrementalStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctx nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count) { + if (mObserver) { + // provide nsIIncrementalStreamLoader::request during call to OnStreamComplete + mRequest = request; + } uint32_t countRead; - return inStr->ReadSegments(WriteSegmentFun, this, count, &countRead); + nsresult rv = inStr->ReadSegments(WriteSegmentFun, this, count, &countRead); + mRequest = 0; + return rv; } void diff --git a/netwerk/base/nsIncrementalStreamLoader.h b/netwerk/base/nsIncrementalStreamLoader.h index 40fa0721daef..988e87d820fa 100644 --- a/netwerk/base/nsIncrementalStreamLoader.h +++ b/netwerk/base/nsIncrementalStreamLoader.h @@ -46,6 +46,9 @@ protected: // Buffer to accumulate incoming data. We preallocate if contentSize is // available. mozilla::Vector mData; + + // Number of consumed bytes from the mData. + size_t mBytesConsumed; }; #endif // nsIncrementalStreamLoader_h__ diff --git a/netwerk/test/unit/test_bug1218029.js b/netwerk/test/unit/test_bug1218029.js new file mode 100644 index 000000000000..cbab52797e84 --- /dev/null +++ b/netwerk/test/unit/test_bug1218029.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var tests = [ + {data: '', chunks: [], status: Cr.NS_OK, consume: [], + dataChunks: ['']}, + {data: 'TWO-PARTS', chunks: [4, 5], status: Cr.NS_OK, consume: [4, 5], + dataChunks: ['TWO-', 'PARTS', '']}, + {data: 'TWO-PARTS', chunks: [4, 5], status: Cr.NS_OK, consume: [0, 0], + dataChunks: ['TWO-', 'TWO-PARTS', 'TWO-PARTS']}, + {data: '3-PARTS', chunks: [1, 1, 5], status: Cr.NS_OK, consume: [0, 2, 5], + dataChunks: ['3', '3-', 'PARTS', '']}, + {data: 'ALL-AT-ONCE', chunks: [11], status: Cr.NS_OK, consume: [0], + dataChunks: ['ALL-AT-ONCE', 'ALL-AT-ONCE']}, + {data: 'ALL-AT-ONCE', chunks: [11], status: Cr.NS_OK, consume: [11], + dataChunks: ['ALL-AT-ONCE', '']}, + {data: 'ERROR', chunks: [1], status: Cr.NS_ERROR_OUT_OF_MEMORY, consume: [0], + dataChunks: ['E', 'E']} +]; + +/** + * @typedef TestData + * @property {string} data - data for the test. + * @property {Array} chunks - lengths of the chunks that are incrementally sent + * to the loader. + * @property {number} status - final status sent on onStopRequest. + * @property {Array} consume - lengths of consumed data that is reported at + * the onIncrementalData callback. + * @property {Array} dataChunks - data chunks that are reported at the + * onIncrementalData and onStreamComplete callbacks. + */ + +function execute_test(test) { + let stream = Cc["@mozilla.org/io/string-input-stream;1"]. + createInstance(Ci.nsIStringInputStream); + stream.data = test.data; + + let channel = { + contentLength: -1, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel]) + }; + + let chunkIndex = 0; + + let observer = { + onStreamComplete: function(loader, context, status, length, data) { + equal(chunkIndex, test.dataChunks.length - 1); + var expectedChunk = test.dataChunks[chunkIndex]; + equal(length, expectedChunk.length); + equal(String.fromCharCode.apply(null, data), expectedChunk); + + equal(status, test.status); + }, + onIncrementalData: function (loader, context, length, data, consumed) { + ok(chunkIndex < test.dataChunks.length - 1); + var expectedChunk = test.dataChunks[chunkIndex]; + equal(length, expectedChunk.length); + equal(String.fromCharCode.apply(null, data), expectedChunk); + + consumed.value = test.consume[chunkIndex]; + chunkIndex++; + }, + QueryInterface: + XPCOMUtils.generateQI([Ci.nsIIncrementalStreamLoaderObserver]) + }; + + let listener = Cc["@mozilla.org/network/incremental-stream-loader;1"] + .createInstance(Ci.nsIIncrementalStreamLoader); + listener.init(observer); + + listener.onStartRequest(channel, null); + var offset = 0; + test.chunks.forEach(function (chunkLength) { + listener.onDataAvailable(channel, null, stream, offset, chunkLength); + offset += chunkLength; + }); + listener.onStopRequest(channel, null, test.status); +} + +function run_test() { + tests.forEach(execute_test); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 941f4620e7be..09cf4de13b37 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -169,6 +169,7 @@ skip-if = os == "android" skip-if = bits != 32 [test_bug935499.js] [test_bug1064258.js] +[test_bug1218029.js] [test_udpsocket.js] [test_doomentry.js] [test_cacheflags.js] From aeaf497a64b425429033e8202dd269f17c8e9f85 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 30 Nov 2015 08:54:40 -0600 Subject: [PATCH 033/102] Bug 1218029 - Adds SRICheckDataVerifier for progressing data handling. r=francois --HG-- extra : commitid : DLkHFWfJFxT --- dom/security/SRICheck.cpp | 304 +++++++++++++++++++++++--------------- dom/security/SRICheck.h | 26 ++++ 2 files changed, 213 insertions(+), 117 deletions(-) diff --git a/dom/security/SRICheck.cpp b/dom/security/SRICheck.cpp index 471669db0f5f..733ec3c9f804 100644 --- a/dom/security/SRICheck.cpp +++ b/dom/security/SRICheck.cpp @@ -11,7 +11,6 @@ #include "mozilla/Preferences.h" #include "nsContentUtils.h" #include "nsIChannel.h" -#include "nsICryptoHash.h" #include "nsIDocument.h" #include "nsIProtocolHandler.h" #include "nsIScriptError.h" @@ -97,65 +96,6 @@ IsEligible(nsIChannel* aChannel, const CORSMode aCORSMode, return NS_ERROR_SRI_NOT_ELIGIBLE; } -/** - * Compute the hash of a sub-resource and compare it with the expected - * value. - */ -static nsresult -VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, - uint32_t aStringLen, const uint8_t* aString, - const nsIDocument* aDocument) -{ - NS_ENSURE_ARG_POINTER(aString); - NS_ENSURE_ARG_POINTER(aDocument); - - nsAutoCString base64Hash; - aMetadata.GetHash(aHashIndex, &base64Hash); - SRILOG(("SRICheck::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); - - nsAutoCString binaryHash; - if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "InvalidIntegrityBase64"); - return NS_ERROR_SRI_CORRUPT; - } - - uint32_t hashLength; - int8_t hashType; - aMetadata.GetHashType(&hashType, &hashLength); - if (binaryHash.Length() != hashLength) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "InvalidIntegrityLength"); - return NS_ERROR_SRI_CORRUPT; - } - - nsresult rv; - nsCOMPtr cryptoHash = - do_CreateInstance("@mozilla.org/security/hash;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = cryptoHash->Init(hashType); - NS_ENSURE_SUCCESS(rv, rv); - rv = cryptoHash->Update(aString, aStringLen); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString computedHash; - rv = cryptoHash->Finish(false, computedHash); - NS_ENSURE_SUCCESS(rv, rv); - if (!binaryHash.Equals(computedHash)) { - SRILOG(("SRICheck::VerifyHash, hash[%u] did not match", aHashIndex)); - return NS_ERROR_SRI_CORRUPT; - } - - SRILOG(("SRICheck::VerifyHash, hash[%u] verified successfully", aHashIndex)); - return NS_OK; -} - /* static */ nsresult SRICheck::IntegrityMetadata(const nsAString& aMetadataList, const nsIDocument* aDocument, @@ -239,52 +179,6 @@ SRICheck::IntegrityMetadata(const nsAString& aMetadataList, return NS_OK; } -static nsresult -VerifyIntegrityInternal(const SRIMetadata& aMetadata, - nsIChannel* aChannel, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument) -{ - MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller - - // IntegrityMetadata() checks this and returns "no metadata" if - // it's disabled so we should never make it this far - MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); - - if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) { - return NS_ERROR_SRI_NOT_ELIGIBLE; - } - if (!aMetadata.IsValid()) { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "NoValidMetadata"); - return NS_OK; // ignore invalid metadata for forward-compatibility - } - - for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { - if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aStringLen, - aString, aDocument))) { - return NS_OK; // stop at the first valid hash - } - } - - nsAutoCString alg; - aMetadata.GetAlgorithm(&alg); - NS_ConvertUTF8toUTF16 algUTF16(alg); - const char16_t* params[] = { algUTF16.get() }; - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Sub-resource Integrity"), - aDocument, - nsContentUtils::eSECURITY_PROPERTIES, - "IntegrityMismatch", - params, ArrayLength(params)); - return NS_ERROR_SRI_CORRUPT; -} - /* static */ nsresult SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, nsIUnicharStreamLoader* aLoader, @@ -306,13 +200,15 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, originalURI) { originalURI->GetAsciiSpec(requestURL); } - SRILOG(("SRICheck::VerifyIntegrity (unichar stream), url=%s (length=%u)", - requestURL.get(), utf8Hash.Length())); + SRILOG(("SRICheck::VerifyIntegrity (unichar stream)")); } - return VerifyIntegrityInternal(aMetadata, channel, aCORSMode, - utf8Hash.Length(), (uint8_t*)utf8Hash.get(), - aDocument); + SRICheckDataVerifier verifier(aMetadata, aDocument); + nsresult rv; + rv = verifier.Update(utf8Hash.Length(), (uint8_t*)utf8Hash.get()); + NS_ENSURE_SUCCESS(rv, rv); + + return verifier.Verify(aMetadata, channel, aCORSMode, aDocument); } /* static */ nsresult @@ -332,14 +228,188 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, channel = do_QueryInterface(request); if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) { - nsAutoCString requestURL; - request->GetName(requestURL); - SRILOG(("SRICheck::VerifyIntegrity (stream), url=%s (length=%u)", - requestURL.get(), aStringLen)); + SRILOG(("SRICheck::VerifyIntegrity (stream)")); } - return VerifyIntegrityInternal(aMetadata, channel, aCORSMode, - aStringLen, aString, aDocument); + SRICheckDataVerifier verifier(aMetadata, aDocument); + nsresult rv; + rv = verifier.Update(aStringLen, aString); + NS_ENSURE_SUCCESS(rv, rv); + + return verifier.Verify(aMetadata, channel, aCORSMode, aDocument); +} + +////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////// +SRICheckDataVerifier::SRICheckDataVerifier(const SRIMetadata& aMetadata, + const nsIDocument* aDocument) + : mCryptoHash(nullptr), + mBytesHashed(0), + mInvalidMetadata(false), + mComplete(false) +{ + MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller + + // IntegrityMetadata() checks this and returns "no metadata" if + // it's disabled so we should never make it this far + MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); + + if (!aMetadata.IsValid()) { + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "NoValidMetadata"); + mInvalidMetadata = true; + return; // ignore invalid metadata for forward-compatibility + } + + uint32_t hashLength; + aMetadata.GetHashType(&mHashType, &hashLength); +} + +nsresult +SRICheckDataVerifier::EnsureCryptoHash() +{ + MOZ_ASSERT(!mInvalidMetadata); + + if (mCryptoHash) { + return NS_OK; + } + + nsresult rv; + nsCOMPtr cryptoHash = + do_CreateInstance("@mozilla.org/security/hash;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = cryptoHash->Init(mHashType); + NS_ENSURE_SUCCESS(rv, rv); + + mCryptoHash = cryptoHash; + return NS_OK; +} + +nsresult +SRICheckDataVerifier::Update(uint32_t aStringLen, const uint8_t* aString) +{ + NS_ENSURE_ARG_POINTER(aString); + if (mInvalidMetadata) { + return NS_OK; // ignoring any data updates, see mInvalidMetadata usage + } + + nsresult rv; + rv = EnsureCryptoHash(); + NS_ENSURE_SUCCESS(rv, rv); + + mBytesHashed += aStringLen; + + return mCryptoHash->Update(aString, aStringLen); +} + +nsresult +SRICheckDataVerifier::Finish() +{ + if (mInvalidMetadata || mComplete) { + return NS_OK; // already finished or invalid metadata + } + + nsresult rv; + rv = EnsureCryptoHash(); // we need computed hash even for 0-length data + NS_ENSURE_SUCCESS(rv, rv); + + rv = mCryptoHash->Finish(false, mComputedHash); + mCryptoHash = nullptr; + mComplete = true; + return rv; +} + +nsresult +SRICheckDataVerifier::VerifyHash(const SRIMetadata& aMetadata, + uint32_t aHashIndex, + const nsIDocument* aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + nsAutoCString base64Hash; + aMetadata.GetHash(aHashIndex, &base64Hash); + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); + + nsAutoCString binaryHash; + if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "InvalidIntegrityBase64"); + return NS_ERROR_SRI_CORRUPT; + } + + uint32_t hashLength; + int8_t hashType; + aMetadata.GetHashType(&hashType, &hashLength); + if (binaryHash.Length() != hashLength) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "InvalidIntegrityLength"); + return NS_ERROR_SRI_CORRUPT; + } + + if (!binaryHash.Equals(mComputedHash)) { + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] did not match", aHashIndex)); + return NS_ERROR_SRI_CORRUPT; + } + + SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] verified successfully", aHashIndex)); + return NS_OK; +} + +nsresult +SRICheckDataVerifier::Verify(const SRIMetadata& aMetadata, + nsIChannel* aChannel, + const CORSMode aCORSMode, + const nsIDocument* aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) { + nsAutoCString requestURL; + nsCOMPtr request; + request = do_QueryInterface(aChannel); + request->GetName(requestURL); + SRILOG(("SRICheckDataVerifier::Verify, url=%s (length=%lu)", + requestURL.get(), mBytesHashed)); + } + + nsresult rv = Finish(); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) { + return NS_ERROR_SRI_NOT_ELIGIBLE; + } + + if (mInvalidMetadata) { + return NS_OK; // ignore invalid metadata for forward-compatibility + } + + for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { + if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aDocument))) { + return NS_OK; // stop at the first valid hash + } + } + + nsAutoCString alg; + aMetadata.GetAlgorithm(&alg); + NS_ConvertUTF8toUTF16 algUTF16(alg); + const char16_t* params[] = { algUTF16.get() }; + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Sub-resource Integrity"), + aDocument, + nsContentUtils::eSECURITY_PROPERTIES, + "IntegrityMismatch", + params, ArrayLength(params)); + return NS_ERROR_SRI_CORRUPT; } } // namespace dom diff --git a/dom/security/SRICheck.h b/dom/security/SRICheck.h index 0aecba50fb70..0038046f712b 100644 --- a/dom/security/SRICheck.h +++ b/dom/security/SRICheck.h @@ -9,8 +9,10 @@ #include "mozilla/CORSMode.h" #include "nsCOMPtr.h" +#include "nsICryptoHash.h" #include "SRIMetadata.h" +class nsIChannel; class nsIDocument; class nsIIncrementalStreamLoader; class nsIUnicharStreamLoader; @@ -54,6 +56,30 @@ public: const nsIDocument* aDocument); }; +class SRICheckDataVerifier final +{ + public: + SRICheckDataVerifier(const SRIMetadata& aMetadata, + const nsIDocument* aDocument); + + nsresult Update(uint32_t aStringLen, const uint8_t* aString); + nsresult Verify(const SRIMetadata& aMetadata, nsIChannel* aChannel, + const CORSMode aCORSMode, const nsIDocument* aDocument); + + private: + nsCOMPtr mCryptoHash; + nsAutoCString mComputedHash; + size_t mBytesHashed; + int8_t mHashType; + bool mInvalidMetadata; + bool mComplete; + + nsresult EnsureCryptoHash(); + nsresult Finish(); + nsresult VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, + const nsIDocument* aDocument); +}; + } // namespace dom } // namespace mozilla From 5576308d8c29e0361cb3e94aac280ec0db0ee325 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 30 Nov 2015 08:54:52 -0600 Subject: [PATCH 034/102] Bug 1218029 - Implements progressive Unicode chars decoding in nsScriptLoader. r=djvj --HG-- extra : commitid : 4fqBUFXilM5 --- dom/base/nsScriptLoader.cpp | 235 +++++++++++++++++++++++++++++------- dom/base/nsScriptLoader.h | 51 ++++++-- dom/security/SRICheck.cpp | 28 ----- dom/security/SRICheck.h | 12 -- 4 files changed, 236 insertions(+), 90 deletions(-) diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 7162073ee4e7..d80574247a91 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -14,7 +14,6 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "xpcpublic.h" -#include "nsIUnicodeDecoder.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/ScriptSettings.h" @@ -54,7 +53,6 @@ #include "mozilla/Attributes.h" #include "mozilla/unused.h" -#include "mozilla/dom/SRICheck.h" #include "nsIScriptError.h" using namespace mozilla; @@ -352,7 +350,13 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, timedChannel->SetInitiatorType(NS_LITERAL_STRING("script")); } - RefPtr handler = new nsScriptLoadHandler(this, aRequest); + nsAutoPtr sriDataVerifier; + if (!aRequest->mIntegrity.IsEmpty()) { + sriDataVerifier = new SRICheckDataVerifier(aRequest->mIntegrity, mDocument); + } + + RefPtr handler = + new nsScriptLoadHandler(this, aRequest, sriDataVerifier.forget()); nsCOMPtr loader; rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler); @@ -1410,20 +1414,33 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, nsresult nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, - nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + nsresult aChannelStatus, + nsresult aSRIStatus, + mozilla::Vector &aString, + mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier) { nsScriptLoadRequest* request = static_cast(aContext); NS_ASSERTION(request, "null request in stream complete handler"); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); - nsresult rv = NS_ERROR_SRI_CORRUPT; - if (request->mIntegrity.IsEmpty() || - NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity, aLoader, - request->mCORSMode, aStringLen, - aString, mDocument))) { - rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString); + nsresult rv = NS_OK; + if (!request->mIntegrity.IsEmpty() && + NS_SUCCEEDED((rv = aSRIStatus))) { + MOZ_ASSERT(aSRIDataVerifier); + + nsCOMPtr channelRequest; + aLoader->GetRequest(getter_AddRefs(channelRequest)); + nsCOMPtr channel; + channel = do_QueryInterface(channelRequest); + + if (NS_FAILED(aSRIDataVerifier->Verify(request->mIntegrity, channel, + request->mCORSMode, mDocument))) { + rv = NS_ERROR_SRI_CORRUPT; + } + } + + if (NS_SUCCEEDED(rv)) { + rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString); } if (NS_FAILED(rv)) { @@ -1466,16 +1483,12 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, } else { mPreloads.RemoveElement(request, PreloadRequestComparator()); } - rv = NS_OK; - } else { - free(const_cast(aString)); - rv = NS_SUCCESS_ADOPTED_DATA; } // Process our request and/or any pending ones ProcessPendingRequests(); - return rv; + return NS_OK; } void @@ -1506,8 +1519,7 @@ nsresult nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIIncrementalStreamLoader* aLoader, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + mozilla::Vector &aString) { if (NS_FAILED(aStatus)) { return aStatus; @@ -1555,21 +1567,9 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, NS_ENSURE_SUCCESS(rv, rv); } - if (aStringLen) { - // Check the charset attribute to determine script charset. - nsAutoString hintCharset; - if (!aRequest->IsPreload()) { - aRequest->mElement->GetScriptCharset(hintCharset); - } else { - nsTArray::index_type i = - mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator()); - NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping"); - hintCharset = mPreloads[i].mCharset; - } - rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument, - aRequest->mScriptTextBuf, aRequest->mScriptTextLength); - - NS_ENSURE_SUCCESS(rv, rv); + if (!aString.empty()) { + aRequest->mScriptTextLength = aString.length(); + aRequest->mScriptTextBuf = aString.extractRawBuffer(); } // This assertion could fire errorously if we ran out of memory when @@ -1714,9 +1714,14 @@ nsScriptLoader::MaybeRemovedDeferRequests() ////////////////////////////////////////////////////////////// nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader, - nsScriptLoadRequest *aRequest) + nsScriptLoadRequest *aRequest, + mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier) : mScriptLoader(aScriptLoader), - mRequest(aRequest) + mRequest(aRequest), + mSRIDataVerifier(aSRIDataVerifier), + mSRIStatus(NS_OK), + mDecoder(), + mBuffer() {} nsScriptLoadHandler::~nsScriptLoadHandler() @@ -1731,18 +1736,164 @@ nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, const uint8_t* aData, uint32_t *aConsumedLength) { + if (mRequest->IsCanceled()) { + // If request cancelled, ignore any incoming data. + *aConsumedLength = aDataLength; + return NS_OK; + } + + if (!EnsureDecoder(aLoader, aData, aDataLength, + /* aEndOfStream = */ false)) { + return NS_OK; + } + + // Below we will/shall consume entire data chunk. + *aConsumedLength = aDataLength; + + // Decoder has already been initialized. -- trying to decode all loaded bytes. + nsresult rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ false); + NS_ENSURE_SUCCESS(rv, rv); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + + return rv; +} + +nsresult +nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + int32_t srcLen = aDataLength; + const char* src = reinterpret_cast(aData); + int32_t dstLen; + nsresult rv = + mDecoder->GetMaxLength(src, srcLen, &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t haveRead = mBuffer.length(); + uint32_t capacity = haveRead + dstLen; + if (!mBuffer.reserve(capacity)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = mDecoder->Convert(src, + &srcLen, + mBuffer.begin() + haveRead, + &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + haveRead += dstLen; + MOZ_ASSERT(haveRead <= capacity, "mDecoder produced more data than expected"); + mBuffer.resizeUninitialized(haveRead); + return NS_OK; } +bool +nsScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + // Check if decoder has already been created. + if (mDecoder) { + return true; + } + + nsAutoCString charset; + + // Determine if BOM check should be done. This occurs either + // if end-of-stream has been reached, or at least 3 bytes have + // been read from input. + if (!aEndOfStream && (aDataLength < 3)) { + return false; + } + + // Do BOM detection. + if (DetectByteOrderMark(aData, aDataLength, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // BOM detection failed, check content stream for charset. + nsCOMPtr req; + nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); + NS_ASSERTION(req, "StreamLoader's request went away prematurely"); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr channel = do_QueryInterface(req); + + if (channel && + NS_SUCCEEDED(channel->GetContentCharset(charset)) && + EncodingUtils::FindEncodingForLabel(charset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Check the hint charset from the script element or preload + // request. + nsAutoString hintCharset; + if (!mRequest->IsPreload()) { + mRequest->mElement->GetScriptCharset(hintCharset); + } else { + nsTArray::index_type i = + mScriptLoader->mPreloads.IndexOf(mRequest, 0, + nsScriptLoader::PreloadRequestComparator()); + + NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex, + "Incorrect preload bookkeeping"); + hintCharset = mScriptLoader->mPreloads[i].mCharset; + } + + if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Get the charset from the charset of the document. + if (mScriptLoader->mDocument) { + charset = mScriptLoader->mDocument->GetDocumentCharacterSet(); + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Curiously, there are various callers that don't pass aDocument. The + // fallback in the old code was ISO-8859-1, which behaved like + // windows-1252. Saying windows-1252 for clarity and for compliance + // with the Encoding Standard. + charset = "windows-1252"; + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; +} + NS_IMETHODIMP nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString) + uint32_t aDataLength, + const uint8_t* aData) { - // pass arguments through except for the aContext, - // we have to mediate and use mRequest instead. - return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, - aStringLen, aString); + if (!mRequest->IsCanceled()) { + DebugOnly encoderSet = + EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true); + MOZ_ASSERT(encoderSet); + DebugOnly rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ true); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + } + + // we have to mediate and use mRequest. + return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus, + mBuffer, mSRIDataVerifier); } diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index 8261c49cb0cd..70d730e417af 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -12,6 +12,7 @@ #define __nsScriptLoader_h__ #include "nsCOMPtr.h" +#include "nsIUnicodeDecoder.h" #include "nsIScriptElement.h" #include "nsCOMArray.h" #include "nsTArray.h" @@ -20,8 +21,10 @@ #include "nsIIncrementalStreamLoader.h" #include "mozilla/CORSMode.h" #include "mozilla/dom/SRIMetadata.h" +#include "mozilla/dom/SRICheck.h" #include "mozilla/LinkedList.h" #include "mozilla/net/ReferrerPolicy.h" +#include "mozilla/Vector.h" class nsScriptLoadRequestList; class nsIURI; @@ -217,6 +220,7 @@ class nsScriptLoader final : public nsISupports }; friend class nsScriptRequestProcessor; + friend class nsScriptLoadHandler; friend class AutoCurrentScriptUpdater; public: @@ -348,9 +352,10 @@ public: */ nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsISupports* aContext, - nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString); + nsresult aChannelStatus, + nsresult aSRIStatus, + mozilla::Vector &aString, + mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier); /** * Processes any pending requests that are ready for processing. @@ -501,8 +506,7 @@ private: nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIIncrementalStreamLoader* aLoader, nsresult aStatus, - uint32_t aStringLen, - const uint8_t* aString); + mozilla::Vector &aString); void AddDeferRequest(nsScriptLoadRequest* aRequest); bool MaybeRemovedDeferRequests(); @@ -552,7 +556,8 @@ class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver { public: explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader, - nsScriptLoadRequest *aRequest); + nsScriptLoadRequest *aRequest, + mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier); NS_DECL_ISUPPORTS NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER @@ -560,8 +565,38 @@ public: private: virtual ~nsScriptLoadHandler(); - RefPtr mScriptLoader; - RefPtr mRequest; + /* + * Try to decode some raw data. + */ + nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + /* + * Discover the charset by looking at the stream data, the script + * tag, and other indicators. Returns true if charset has been + * discovered. + */ + bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + // ScriptLoader which will handle the parsed script. + RefPtr mScriptLoader; + + // The nsScriptLoadRequest for this load. + RefPtr mRequest; + + // SRI data verifier. + nsAutoPtr mSRIDataVerifier; + + // Status of SRI data operations. + nsresult mSRIStatus; + + // Unicode decoder for charset. + nsCOMPtr mDecoder; + + // Accumulated decoded char buffer. + mozilla::Vector mBuffer; }; class nsAutoScriptLoaderDisabler diff --git a/dom/security/SRICheck.cpp b/dom/security/SRICheck.cpp index 733ec3c9f804..a135502394f9 100644 --- a/dom/security/SRICheck.cpp +++ b/dom/security/SRICheck.cpp @@ -211,34 +211,6 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, return verifier.Verify(aMetadata, channel, aCORSMode, aDocument); } -/* static */ nsresult -SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, - nsIIncrementalStreamLoader* aLoader, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument) -{ - NS_ENSURE_ARG_POINTER(aLoader); - - nsCOMPtr request; - aLoader->GetRequest(getter_AddRefs(request)); - NS_ENSURE_ARG_POINTER(request); - nsCOMPtr channel; - channel = do_QueryInterface(request); - - if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) { - SRILOG(("SRICheck::VerifyIntegrity (stream)")); - } - - SRICheckDataVerifier verifier(aMetadata, aDocument); - nsresult rv; - rv = verifier.Update(aStringLen, aString); - NS_ENSURE_SUCCESS(rv, rv); - - return verifier.Verify(aMetadata, channel, aCORSMode, aDocument); -} - ////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////// diff --git a/dom/security/SRICheck.h b/dom/security/SRICheck.h index 0038046f712b..8734c1f99ba9 100644 --- a/dom/security/SRICheck.h +++ b/dom/security/SRICheck.h @@ -14,7 +14,6 @@ class nsIChannel; class nsIDocument; -class nsIIncrementalStreamLoader; class nsIUnicharStreamLoader; namespace mozilla { @@ -43,17 +42,6 @@ public: const CORSMode aCORSMode, const nsAString& aString, const nsIDocument* aDocument); - - /** - * Process the integrity attribute of the element. A result of false - * must prevent the resource from loading. - */ - static nsresult VerifyIntegrity(const SRIMetadata& aMetadata, - nsIIncrementalStreamLoader* aLoader, - const CORSMode aCORSMode, - uint32_t aStringLen, - const uint8_t* aString, - const nsIDocument* aDocument); }; class SRICheckDataVerifier final From ed2c2e5b1c04e3b7f70b8c572e5393c4d289d9c8 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Tue, 1 Dec 2015 13:48:18 -0800 Subject: [PATCH 035/102] Bug 1227604 - Fix compositor event order; r=snorp When the queue only contains compositor events, a compositor event should go to the back of the queue to maintain order. --HG-- extra : commitid : I7bHvJCe68o --- widget/android/nsAppShell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index f7f76565efb2..49d857f74788 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -837,7 +837,7 @@ nsAppShell::LegacyGeckoEvent::PostTo(mozilla::LinkedList& queue) if (event) { event->setPrevious(this); } else { - queue.insertFront(this); + queue.insertBack(this); } EVLOG("nsAppShell: Inserting compositor event %d to maintain priority order", ae->Type()); } From 77930e373bdc4a9613d1fdcdedade92586e8fcc2 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Fri, 27 Nov 2015 07:37:33 +0000 Subject: [PATCH 036/102] Bug 1228230 - Rely more on top-level configure auto-detection. r=qdot --HG-- extra : commitid : zmjwDqqQv6 --- media/libav/config_darwin.asm | 4 ---- media/libav/config_darwin.h | 4 ---- media/libav/config_unix.asm | 4 ---- media/libav/config_unix.h | 4 ---- media/libav/config_win.asm | 4 ---- media/libav/config_win.h | 4 ---- 6 files changed, 24 deletions(-) diff --git a/media/libav/config_darwin.asm b/media/libav/config_darwin.asm index 5ad7e45f9783..89413c594ec7 100644 --- a/media/libav/config_darwin.asm +++ b/media/libav/config_darwin.asm @@ -128,7 +128,6 @@ %define HAVE_MACH_MACH_TIME_H 1 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 0 %define HAVE_POLL_H 1 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -190,15 +189,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 1 %define HAVE_MACH_ABSOLUTE_TIME 1 %define HAVE_MAPVIEWOFFILE 0 -%define HAVE_MEMALIGN 0 %define HAVE_MKSTEMP 1 %define HAVE_MMAP 1 %define HAVE_MPROTECT 1 %define HAVE_NANOSLEEP 1 -%define HAVE_POSIX_MEMALIGN 1 %define HAVE_SCHED_GETAFFINITY 0 %define HAVE_SETCONSOLETEXTATTRIBUTE 0 %define HAVE_SETMODE 0 diff --git a/media/libav/config_darwin.h b/media/libav/config_darwin.h index 55df762a13e4..ae7844f3aa92 100644 --- a/media/libav/config_darwin.h +++ b/media/libav/config_darwin.h @@ -138,7 +138,6 @@ #define HAVE_MACH_MACH_TIME_H 1 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 0 #define HAVE_POLL_H 1 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -200,15 +199,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 1 #define HAVE_MACH_ABSOLUTE_TIME 1 #define HAVE_MAPVIEWOFFILE 0 -#define HAVE_MEMALIGN 0 #define HAVE_MKSTEMP 1 #define HAVE_MMAP 1 #define HAVE_MPROTECT 1 #define HAVE_NANOSLEEP 1 -#define HAVE_POSIX_MEMALIGN 1 #define HAVE_SCHED_GETAFFINITY 0 #define HAVE_SETCONSOLETEXTATTRIBUTE 0 #define HAVE_SETMODE 0 diff --git a/media/libav/config_unix.asm b/media/libav/config_unix.asm index 7f16ba817b1c..c08045a0b4db 100644 --- a/media/libav/config_unix.asm +++ b/media/libav/config_unix.asm @@ -134,7 +134,6 @@ %define HAVE_MACH_MACH_TIME_H 0 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 1 %define HAVE_POLL_H 1 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -196,15 +195,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 1 %define HAVE_MACH_ABSOLUTE_TIME 0 %define HAVE_MAPVIEWOFFILE 0 -%define HAVE_MEMALIGN 1 %define HAVE_MKSTEMP 1 %define HAVE_MMAP 1 %define HAVE_MPROTECT 1 %define HAVE_NANOSLEEP 1 -%define HAVE_POSIX_MEMALIGN 1 %define HAVE_SCHED_GETAFFINITY 1 %define HAVE_SETCONSOLETEXTATTRIBUTE 0 %define HAVE_SETMODE 0 diff --git a/media/libav/config_unix.h b/media/libav/config_unix.h index 668a02ef2ca9..3921293a0ac4 100644 --- a/media/libav/config_unix.h +++ b/media/libav/config_unix.h @@ -144,7 +144,6 @@ #define HAVE_MACH_MACH_TIME_H 0 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 1 #define HAVE_POLL_H 1 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -206,15 +205,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 1 #define HAVE_MACH_ABSOLUTE_TIME 0 #define HAVE_MAPVIEWOFFILE 0 -#define HAVE_MEMALIGN 1 #define HAVE_MKSTEMP 1 #define HAVE_MMAP 1 #define HAVE_MPROTECT 1 #define HAVE_NANOSLEEP 1 -#define HAVE_POSIX_MEMALIGN 1 #define HAVE_SCHED_GETAFFINITY 1 #define HAVE_SETCONSOLETEXTATTRIBUTE 0 #define HAVE_SETMODE 0 diff --git a/media/libav/config_win.asm b/media/libav/config_win.asm index 37a802f4c06d..7ea033371b54 100644 --- a/media/libav/config_win.asm +++ b/media/libav/config_win.asm @@ -138,7 +138,6 @@ %define HAVE_MACH_MACH_TIME_H 0 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 1 %define HAVE_POLL_H 0 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -200,15 +199,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 0 %define HAVE_MACH_ABSOLUTE_TIME 0 %define HAVE_MAPVIEWOFFILE 1 -%define HAVE_MEMALIGN 0 %define HAVE_MKSTEMP 0 %define HAVE_MMAP 0 %define HAVE_MPROTECT 0 %define HAVE_NANOSLEEP 0 -%define HAVE_POSIX_MEMALIGN 0 %define HAVE_SCHED_GETAFFINITY 0 %define HAVE_SETCONSOLETEXTATTRIBUTE 1 %define HAVE_SETMODE 1 diff --git a/media/libav/config_win.h b/media/libav/config_win.h index b64f19c8d6f4..b40defe720b2 100644 --- a/media/libav/config_win.h +++ b/media/libav/config_win.h @@ -153,7 +153,6 @@ #define HAVE_MACH_MACH_TIME_H 0 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 1 #define HAVE_POLL_H 0 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -215,15 +214,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 0 #define HAVE_MACH_ABSOLUTE_TIME 0 #define HAVE_MAPVIEWOFFILE 1 -#define HAVE_MEMALIGN 0 #define HAVE_MKSTEMP 0 #define HAVE_MMAP 0 #define HAVE_MPROTECT 0 #define HAVE_NANOSLEEP 0 -#define HAVE_POSIX_MEMALIGN 0 #define HAVE_SCHED_GETAFFINITY 0 #define HAVE_SETCONSOLETEXTATTRIBUTE 1 #define HAVE_SETMODE 1 From 83e664a4cd348e77b00fc01435fbe105da3ec5af Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 1 Dec 2015 13:45:49 -0800 Subject: [PATCH 037/102] Add APZ support for mousewheel.acceleration prefs. (bug 1214170 part 1, r=kats) --HG-- extra : rebase_source : b97121b98c3e42a83135f7e8fa186575b4fbfcb5 --- dom/events/WheelHandlingHelper.cpp | 12 ++++-------- dom/events/WheelHandlingHelper.h | 2 -- gfx/layers/apz/src/APZCTreeManager.cpp | 4 ++-- gfx/layers/apz/src/AsyncPanZoomController.cpp | 15 +++++++++++++++ gfx/layers/apz/src/InputBlockState.cpp | 16 +++++++++++++++- gfx/layers/apz/src/InputBlockState.h | 3 ++- gfx/layers/apz/src/InputQueue.cpp | 8 +++++--- gfx/thebes/gfxPrefs.h | 4 ++++ layout/generic/AsyncScrollBase.h | 13 +++++++++++++ widget/InputData.h | 5 +++++ 10 files changed, 65 insertions(+), 17 deletions(-) diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp index 84093a824830..86e74de2f0b1 100644 --- a/dom/events/WheelHandlingHelper.cpp +++ b/dom/events/WheelHandlingHelper.cpp @@ -21,6 +21,7 @@ #include "nsPresContext.h" #include "prtime.h" #include "Units.h" +#include "AsyncScrollBase.h" namespace mozilla { @@ -137,7 +138,7 @@ WheelTransaction::UpdateTransaction(WidgetWheelEvent* aEvent) SetTimeout(); - if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeout)) { + if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeoutMs)) { sScrollSeriesCounter = 0; } sScrollSeriesCounter++; @@ -383,14 +384,9 @@ WheelTransaction::AccelerateWheelDelta(WidgetWheelEvent* aEvent, } /* static */ double -WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta, - int32_t aFactor) +WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor) { - if (aDelta == 0.0) { - return 0; - } - - return (aDelta * sScrollSeriesCounter * (double)aFactor / 10); + return mozilla::ComputeAcceleratedWheelDelta(aDelta, sScrollSeriesCounter, aFactor); } /* static */ int32_t diff --git a/dom/events/WheelHandlingHelper.h b/dom/events/WheelHandlingHelper.h index 62ee3c31b4ff..b88aa23b2d4c 100644 --- a/dom/events/WheelHandlingHelper.h +++ b/dom/events/WheelHandlingHelper.h @@ -151,8 +151,6 @@ public: bool aAllowScrollSpeedOverride); protected: - static const uint32_t kScrollSeriesTimeout = 80; // in milliseconds - static void BeginTransaction(nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent); // Be careful, UpdateTransaction may fire a DOM event, therefore, the target diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 918bd1d9610d..4d7e7df26d58 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -625,8 +625,8 @@ static bool WillHandleWheelEvent(WidgetWheelEvent* aEvent) { return EventStateManager::WheelEventIsScrollAction(aEvent) && - (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE - || aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) && + (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE || + aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) && !EventStateManager::WheelEventNeedsDeltaMultipliers(aEvent); } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 52f5dc27e50d..173ecdab1fb7 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1616,6 +1616,21 @@ AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) cons } } + // If this is a line scroll, and this event was part of a scroll series, then + // it might need extra acceleration. See WheelHandlingHelper.cpp. + if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE && + aEvent.mScrollSeriesNumber > 0) + { + int32_t start = gfxPrefs::MouseWheelAccelerationStart(); + if (start >= 0 && aEvent.mScrollSeriesNumber >= uint32_t(start)) { + int32_t factor = gfxPrefs::MouseWheelAccelerationFactor(); + if (factor > 0) { + delta.x = ComputeAcceleratedWheelDelta(delta.x, aEvent.mScrollSeriesNumber, factor); + delta.y = ComputeAcceleratedWheelDelta(delta.y, aEvent.mScrollSeriesNumber, factor); + } + } + } + if (Abs(delta.x) > pageScrollSize.width) { delta.x = (delta.x >= 0) ? pageScrollSize.width diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp index c6f60a9e3596..58f3482514eb 100644 --- a/gfx/layers/apz/src/InputBlockState.cpp +++ b/gfx/layers/apz/src/InputBlockState.cpp @@ -6,6 +6,7 @@ #include "InputBlockState.h" #include "AsyncPanZoomController.h" // for AsyncPanZoomController +#include "AsyncScrollBase.h" // for kScrollSeriesTimeoutMs #include "gfxPrefs.h" // for gfxPrefs #include "mozilla/MouseEvents.h" #include "mozilla/SizePrintfMacros.h" // for PRIuSIZE @@ -244,6 +245,7 @@ WheelBlockState::WheelBlockState(const RefPtr& aTargetAp bool aTargetConfirmed, const ScrollWheelInput& aInitialEvent) : CancelableBlockState(aTargetApzc, aTargetConfirmed) + , mScrollSeriesCounter(0) , mTransactionEnded(false) { sLastWheelBlockId = GetBlockId(); @@ -295,7 +297,7 @@ WheelBlockState::SetConfirmedTargetApzc(const RefPtr& aT } void -WheelBlockState::Update(const ScrollWheelInput& aEvent) +WheelBlockState::Update(ScrollWheelInput& aEvent) { // We might not be in a transaction if the block never started in a // transaction - for example, if nothing was scrollable. @@ -303,6 +305,18 @@ WheelBlockState::Update(const ScrollWheelInput& aEvent) return; } + // The current "scroll series" is a like a sub-transaction. It has a separate + // timeout of 80ms. Since we need to compute wheel deltas at different phases + // of a transaction (for example, when it is updated, and later when the + // event action is taken), we affix the scroll series counter to the event. + // This makes GetScrollWheelDelta() consistent. + if (!mLastEventTime.IsNull() && + (aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs) + { + mScrollSeriesCounter = 0; + } + aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter; + // If we can't scroll in the direction of the wheel event, we don't update // the last move time. This allows us to timeout a transaction even if the // mouse isn't moving. diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h index 37e643d9e23b..edcf92cc38d5 100644 --- a/gfx/layers/apz/src/InputBlockState.h +++ b/gfx/layers/apz/src/InputBlockState.h @@ -244,7 +244,7 @@ public: /** * Update the wheel transaction state for a new event. */ - void Update(const ScrollWheelInput& aEvent); + void Update(ScrollWheelInput& aEvent); protected: void UpdateTargetApzc(const RefPtr& aTargetApzc) override; @@ -253,6 +253,7 @@ private: nsTArray mEvents; TimeStamp mLastEventTime; TimeStamp mLastMouseMove; + uint32_t mScrollSeriesCounter; bool mTransactionEnded; }; diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp index 3db4391dd050..7b62a7ff0000 100644 --- a/gfx/layers/apz/src/InputQueue.cpp +++ b/gfx/layers/apz/src/InputQueue.cpp @@ -277,15 +277,17 @@ InputQueue::ReceiveScrollWheelInput(const RefPtr& aTarge *aOutInputBlockId = block->GetBlockId(); } - block->Update(aEvent); + // Copy the event, since WheelBlockState needs to affix a counter. + ScrollWheelInput event(aEvent); + block->Update(event); // Note that the |aTarget| the APZCTM sent us may contradict the confirmed // target set on the block. In this case the confirmed target (which may be // null) should take priority. This is equivalent to just always using the // target (confirmed or not) from the block, which is what // MaybeHandleCurrentBlock() does. - if (!MaybeHandleCurrentBlock(block, aEvent)) { - block->AddEvent(aEvent.AsScrollWheelInput()); + if (!MaybeHandleCurrentBlock(block, event)) { + block->AddEvent(event); } return nsEventStatus_eConsumeDoDefault; diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 80001421e978..ae8ad023b176 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -383,6 +383,10 @@ private: // This and code dependent on it should be removed once containerless scrolling looks stable. DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers", LayoutUseContainersForRootFrames, bool, true); + // These affect how line scrolls from wheel events will be accelerated. + DECL_GFX_PREF(Live, "mousewheel.acceleration.factor", MouseWheelAccelerationFactor, int32_t, -1); + DECL_GFX_PREF(Live, "mousewheel.acceleration.start", MouseWheelAccelerationStart, int32_t, -1); + // This affects whether events will be routed through APZ or not. DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled", MouseWheelHasRootScrollDeltaOverride, bool, false); diff --git a/layout/generic/AsyncScrollBase.h b/layout/generic/AsyncScrollBase.h index 9a1725ba8882..45e9d353037d 100644 --- a/layout/generic/AsyncScrollBase.h +++ b/layout/generic/AsyncScrollBase.h @@ -87,6 +87,19 @@ protected: nsSMILKeySpline mTimingFunctionY; }; +// Helper for accelerated wheel deltas. This can be called from the main thread +// or the APZ Controller thread. +static inline double +ComputeAcceleratedWheelDelta(double aDelta, int32_t aCounter, int32_t aFactor) +{ + if (!aDelta) { + return aDelta; + } + return (aDelta * aCounter * double(aFactor) / 10); +} + +static const uint32_t kScrollSeriesTimeoutMs = 80; // in milliseconds + } // namespace mozilla #endif // mozilla_layout_AsyncScrollBase_h_ diff --git a/widget/InputData.h b/widget/InputData.h index 61d001aff11e..0c1a49794680 100644 --- a/widget/InputData.h +++ b/widget/InputData.h @@ -582,6 +582,7 @@ public: mDeltaY(aDeltaY), mLineOrPageDeltaX(0), mLineOrPageDeltaY(0), + mScrollSeriesNumber(0), mIsMomentum(false) {} @@ -615,6 +616,10 @@ public: int32_t mLineOrPageDeltaX; int32_t mLineOrPageDeltaY; + // Indicates the order in which this event was added to a transaction. The + // first event is 1; if not a member of a transaction, this is 0. + uint32_t mScrollSeriesNumber; + bool mIsMomentum; }; From 0bf441628c581e0d67be338ea6443e2cf9ac69f5 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 1 Dec 2015 13:46:07 -0800 Subject: [PATCH 038/102] Add APZ support for mousewheel delta multiplier prefs. (bug 1214170 part 2, r=kats) --HG-- extra : rebase_source : e2847430422f19d872c446524627cbaa526ffe06 --- dom/events/EventStateManager.cpp | 19 ++++++++++++------- dom/events/EventStateManager.h | 11 +++++++---- gfx/layers/apz/src/APZCTreeManager.cpp | 16 ++++++++++++---- gfx/layers/apz/src/AsyncPanZoomController.cpp | 13 ++++++++++++- widget/InputData.cpp | 9 +++++++++ widget/InputData.h | 8 ++++++++ 6 files changed, 60 insertions(+), 16 deletions(-) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index d2f96c26aa64..697e05016320 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -5716,14 +5716,16 @@ EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta( (mMultiplierY[index] != 1.0 && mMultiplierY[index] != -1.0); } -bool -EventStateManager::WheelPrefs::HasUserPrefsForDelta(WidgetWheelEvent* aEvent) +void +EventStateManager::WheelPrefs::GetUserPrefsForEvent(WidgetWheelEvent* aEvent, + double* aOutMultiplierX, + double* aOutMultiplierY) { Index index = GetIndexFor(aEvent); Init(index); - return mMultiplierX[index] != 1.0 || - mMultiplierY[index] != 1.0; + *aOutMultiplierX = mMultiplierX[index]; + *aOutMultiplierY = mMultiplierY[index]; } bool @@ -5733,10 +5735,13 @@ EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent) WheelPrefs::GetInstance()->ComputeActionFor(aEvent) == WheelPrefs::ACTION_SCROLL; } -bool -EventStateManager::WheelEventNeedsDeltaMultipliers(WidgetWheelEvent* aEvent) +void +EventStateManager::GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent, + double* aOutMultiplierX, + double* aOutMultiplierY) { - return WheelPrefs::GetInstance()->HasUserPrefsForDelta(aEvent); + WheelPrefs::GetInstance()->GetUserPrefsForEvent( + aEvent, aOutMultiplierX, aOutMultiplierY); } bool diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 0575233c05e6..e10e87e3303a 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -242,9 +242,10 @@ public: // Returns true if the given WidgetWheelEvent will resolve to a scroll action. static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent); - // Returns true if user prefs for wheel deltas apply to the given - // WidgetWheelEvent. - static bool WheelEventNeedsDeltaMultipliers(WidgetWheelEvent* aEvent); + // Returns user-set multipliers for a wheel event. + static void GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent, + double* aOutMultiplierX, + double* aOutMultiplierY); // Returns whether or not a frame can be vertically scrolled with a mouse // wheel (as opposed to, say, a selection or touch scroll). @@ -449,7 +450,9 @@ protected: * Returns whether or not ApplyUserPrefsToDelta() would change the delta * values of an event. */ - bool HasUserPrefsForDelta(WidgetWheelEvent* aEvent); + void GetUserPrefsForEvent(WidgetWheelEvent* aEvent, + double* aOutMultiplierX, + double* aOutMultiplierY); /** * If ApplyUserPrefsToDelta() changed the delta values with customized diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 4d7e7df26d58..6989933bfaf5 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -626,8 +626,7 @@ WillHandleWheelEvent(WidgetWheelEvent* aEvent) { return EventStateManager::WheelEventIsScrollAction(aEvent) && (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE || - aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) && - !EventStateManager::WheelEventNeedsDeltaMultipliers(aEvent); + aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL); } static bool @@ -1090,8 +1089,17 @@ APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent, scrollMode, ScrollWheelInput::DeltaTypeForDeltaMode(aEvent.deltaMode), origin, - aEvent.deltaX, - aEvent.deltaY); + aEvent.deltaX, aEvent.deltaY); + + // We add the user multiplier as a separate field, rather than premultiplying + // it, because if the input is converted back to a WidgetWheelEvent, then + // EventStateManager would apply the delta a second time. We could in theory + // work around this by asking ESM to customize the event much sooner, and + // then save the "customizedByUserPrefs" bit on ScrollWheelInput - but for + // now, this seems easier. + EventStateManager::GetUserPrefsForWheelEvent(&aEvent, + &input.mUserDeltaMultiplierX, + &input.mUserDeltaMultiplierY); nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId); aEvent.refPoint.x = input.mOrigin.x; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 173ecdab1fb7..c4da0148a8cf 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1604,7 +1604,18 @@ AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) cons MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type"); } - if (isRootContent && gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) { + // Apply user-set multipliers. + delta.x *= aEvent.mUserDeltaMultiplierX; + delta.y *= aEvent.mUserDeltaMultiplierY; + + // For the conditions under which we allow system scroll overrides, see + // EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction + // and WheelTransaction::OverrideSystemScrollSpeed. + if (isRootContent && + gfxPrefs::MouseWheelHasRootScrollDeltaOverride() && + !aEvent.IsCustomizedByUserPrefs() && + aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE) + { // Only apply delta multipliers if we're increasing the delta. double hfactor = double(gfxPrefs::MouseWheelRootHScrollDeltaFactor()) / 100; double vfactor = double(gfxPrefs::MouseWheelRootVScrollDeltaFactor()) / 100; diff --git a/widget/InputData.cpp b/widget/InputData.cpp index 471717903f91..a0d0bb868e95 100644 --- a/widget/InputData.cpp +++ b/widget/InputData.cpp @@ -376,6 +376,8 @@ ScrollWheelInput::ScrollWheelInput(const WidgetWheelEvent& aWheelEvent) : mDeltaY(aWheelEvent.deltaY), mLineOrPageDeltaX(aWheelEvent.lineOrPageDeltaX), mLineOrPageDeltaY(aWheelEvent.lineOrPageDeltaY), + mUserDeltaMultiplierX(1.0), + mUserDeltaMultiplierY(1.0), mIsMomentum(aWheelEvent.isMomentum) { mOrigin = @@ -415,4 +417,11 @@ ScrollWheelInput::TransformToLocal(const gfx::Matrix4x4& aTransform) return true; } +bool +ScrollWheelInput::IsCustomizedByUserPrefs() const +{ + return mUserDeltaMultiplierX != 1.0 || + mUserDeltaMultiplierY != 1.0; +} + } // namespace mozilla diff --git a/widget/InputData.h b/widget/InputData.h index 0c1a49794680..331f52874a69 100644 --- a/widget/InputData.h +++ b/widget/InputData.h @@ -583,6 +583,8 @@ public: mLineOrPageDeltaX(0), mLineOrPageDeltaY(0), mScrollSeriesNumber(0), + mUserDeltaMultiplierX(1.0), + mUserDeltaMultiplierY(1.0), mIsMomentum(false) {} @@ -591,6 +593,8 @@ public: WidgetWheelEvent ToWidgetWheelEvent(nsIWidget* aWidget) const; bool TransformToLocal(const gfx::Matrix4x4& aTransform); + bool IsCustomizedByUserPrefs() const; + ScrollDeltaType mDeltaType; ScrollMode mScrollMode; ScreenPoint mOrigin; @@ -620,6 +624,10 @@ public: // first event is 1; if not a member of a transaction, this is 0. uint32_t mScrollSeriesNumber; + // User-set delta multipliers. + double mUserDeltaMultiplierX; + double mUserDeltaMultiplierY; + bool mIsMomentum; }; From fa0c646a199faccf6ca4eb3911607c464f06584f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 1 Dec 2015 13:49:01 -0800 Subject: [PATCH 039/102] Remove "bad APZ pref" warnings in about:support. (bug 1214170 part 3, r=kats) --HG-- extra : rebase_source : 77bf9992083045c88c96ae688d7dc949cbdceab1 --- gfx/thebes/gfxPlatform.cpp | 33 --------------------------------- toolkit/content/aboutSupport.js | 10 +--------- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index dec432c14c20..fb92999d9d8f 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2132,34 +2132,6 @@ gfxPlatform::GetDefaultFrameRate() return 60; } -static nsString -DetectBadApzWheelInputPrefs() -{ - static const char *sBadMultiplierPrefs[] = { - "mousewheel.default.delta_multiplier_x", - "mousewheel.with_alt.delta_multiplier_x", - "mousewheel.with_control.delta_multiplier_x", - "mousewheel.with_meta.delta_multiplier_x", - "mousewheel.with_shift.delta_multiplier_x", - "mousewheel.with_win.delta_multiplier_x", - "mousewheel.with_alt.delta_multiplier_y", - "mousewheel.with_control.delta_multiplier_y", - "mousewheel.with_meta.delta_multiplier_y", - "mousewheel.with_shift.delta_multiplier_y", - "mousewheel.with_win.delta_multiplier_y", - }; - - nsString badPref; - for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sBadMultiplierPrefs); i++) { - if (Preferences::GetInt(sBadMultiplierPrefs[i], 100) != 100) { - badPref.AssignASCII(sBadMultiplierPrefs[i]); - break; - } - } - - return badPref; -} - void gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj) { @@ -2168,12 +2140,7 @@ gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj) } if (SupportsApzWheelInput()) { - nsString badPref = DetectBadApzWheelInputPrefs(); - aObj.DefineProperty("ApzWheelInput", 1); - if (badPref.Length()) { - aObj.DefineProperty("ApzWheelInputWarning", badPref); - } } if (SupportsApzTouchInput()) { diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 94e9c09c990f..f3538edacc55 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -206,21 +206,13 @@ var snapshotFormatters = { let out = []; for (let type of ['Wheel', 'Touch', 'Drag']) { let key = 'Apz' + type + 'Input'; - let warningKey = key + 'Warning'; if (!(key in info)) continue; - let badPref = info[warningKey]; delete info[key]; - delete info[warningKey]; - let message; - if (badPref) - message = localizedMsg([type.toLowerCase() + 'Warning', badPref]); - else - message = localizedMsg([type.toLowerCase() + 'Enabled']); - dump(message + ', ' + (type.toLowerCase() + 'Warning') + ', ' + badPref + '\n'); + let message = localizedMsg([type.toLowerCase() + 'Enabled']); out.push(message); } From 469b44b323fdceaec7b79d3116646c0e19501483 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 040/102] Bug 1228670 - Null-check GetContentInsertionFrame() before using it. r=bz --- layout/forms/nsListControlFrame.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 1bac55657ad2..eecfcc624232 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -270,8 +270,10 @@ GetMaxOptionBSize(nsIFrame* aContainer, WritingMode aWM) nscoord optionBSize; if (nsCOMPtr (do_QueryInterface(option->GetContent()))) { - // An optgroup; drill through any scroll frame and recurse. - optionBSize = GetMaxOptionBSize(option->GetContentInsertionFrame(), aWM); + // An optgroup; drill through any scroll frame and recurse. |frame| might + // be null here though if |option| is an anonymous leaf frame of some sort. + auto frame = option->GetContentInsertionFrame(); + optionBSize = frame ? GetMaxOptionBSize(frame, aWM) : 0; } else { // an option optionBSize = option->BSize(aWM); From 42b8b555efd1f924a59ffacab4791387ed01cbea Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 041/102] Bug 1228670 - Crashtest. --- layout/forms/crashtests/1228670.xhtml | 7 +++++++ layout/forms/crashtests/crashtests.list | 1 + 2 files changed, 8 insertions(+) create mode 100644 layout/forms/crashtests/1228670.xhtml diff --git a/layout/forms/crashtests/1228670.xhtml b/layout/forms/crashtests/1228670.xhtml new file mode 100644 index 000000000000..cc8d309c0eae --- /dev/null +++ b/layout/forms/crashtests/1228670.xhtml @@ -0,0 +1,7 @@ + + + + + diff --git a/layout/forms/crashtests/crashtests.list b/layout/forms/crashtests/crashtests.list index 70d14e7f9e4e..49900f279a3b 100644 --- a/layout/forms/crashtests/crashtests.list +++ b/layout/forms/crashtests/crashtests.list @@ -64,3 +64,4 @@ load 1102791.html load 1140216.html load 1182414.html load 1212688.html +load 1228670.xhtml From b65f5f8f9ca360bdb1a45bac5c917f0da771edbb Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 042/102] Bug 1176793 part 1 - [css-grid] Implement margin:auto for grid items. r=dholbert --- layout/generic/nsGridContainerFrame.cpp | 33 +++++++++--- layout/generic/nsHTMLReflowState.cpp | 67 ++++++++++++++----------- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index d1acf79976a1..6c2e01e892c6 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -914,15 +914,36 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, } } + const auto& styleMargin = aRS.mStyleMargin->mMargin; + bool hasAutoMarginStart; + bool hasAutoMarginEnd; + if (aAxis == eLogicalAxisBlock) { + hasAutoMarginStart = styleMargin.GetBStartUnit(wm) == eStyleUnit_Auto; + hasAutoMarginEnd = styleMargin.GetBEndUnit(wm) == eStyleUnit_Auto; + } else { + hasAutoMarginStart = styleMargin.GetIStartUnit(wm) == eStyleUnit_Auto; + hasAutoMarginEnd = styleMargin.GetIEndUnit(wm) == eStyleUnit_Auto; + } + // https://drafts.csswg.org/css-align-3/#overflow-values // This implements = 'safe'. - if (MOZ_UNLIKELY(aOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) { + // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins + if ((MOZ_UNLIKELY(aOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) || + hasAutoMarginStart || hasAutoMarginEnd) { nscoord space = SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize); // XXX we might want to include == 0 here as an optimization - // I need to see what the baseline/last-baseline code looks like first. if (space < 0) { + // "Overflowing elements ignore their auto margins and overflow + // in the end directions" aAlignment = NS_STYLE_ALIGN_START; + } else if (hasAutoMarginEnd) { + aAlignment = hasAutoMarginStart ? NS_STYLE_ALIGN_CENTER + : (aSameSide ? NS_STYLE_ALIGN_START + : NS_STYLE_ALIGN_END); + } else if (hasAutoMarginStart) { + aAlignment = aSameSide ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START; } } @@ -947,15 +968,11 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, aAxis, aCBSize) / 2; break; case NS_STYLE_ALIGN_STRETCH: { + MOZ_ASSERT(!hasAutoMarginStart && !hasAutoMarginEnd); offset = marginStart; - const auto& styleMargin = aRS.mStyleMargin->mMargin; if (aAxis == eLogicalAxisBlock - ? (aRS.mStylePosition->BSize(wm).GetUnit() == eStyleUnit_Auto && - styleMargin.GetBStartUnit(wm) != eStyleUnit_Auto && - styleMargin.GetBEndUnit(wm) != eStyleUnit_Auto) - : (aRS.mStylePosition->ISize(wm).GetUnit() == eStyleUnit_Auto && - styleMargin.GetIStartUnit(wm) != eStyleUnit_Auto && - styleMargin.GetIEndUnit(wm) != eStyleUnit_Auto)) { + ? (aRS.mStylePosition->BSize(wm).GetUnit() == eStyleUnit_Auto) + : (aRS.mStylePosition->ISize(wm).GetUnit() == eStyleUnit_Auto)) { nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm) : aChildSize.ISize(wm); nscoord gap = aCBSize - (size + marginStart + marginEnd); diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 9c26a564d9ea..2a3f428d78d9 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -2289,44 +2289,51 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, ComputeSizeFlags computeSizeFlags = isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap; - // Make sure legend frames with display:block and width:auto still - // shrink-wrap. - // Also shrink-wrap blocks that are orthogonal to their container. - if (isBlock && - ((aFrameType == nsGkAtoms::legendFrame && - frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || - (aFrameType == nsGkAtoms::scrollFrame && - frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) || - (mCBReflowState && - mCBReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)))) { - computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); - } - nsIFrame* parent = frame->GetParent(); nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr; - if (parentFrameType == nsGkAtoms::flexContainerFrame) { - computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); - - // If we're inside of a flex container that needs to measure our - // auto height, pass that information along to ComputeSize(). - if (mFlags.mIsFlexContainerMeasuringHeight) { + if (parentFrameType == nsGkAtoms::gridContainerFrame) { + // Shrink-wrap grid items that will be aligned (rather than stretched) + // in its inline axis. + auto inlineAxisAlignment = wm.IsOrthogonalTo(cbwm) ? + mStylePosition->ComputedAlignSelf(mStyleDisplay, + frame->StyleContext()->GetParent()) : + mStylePosition->ComputedJustifySelf(mStyleDisplay, + frame->StyleContext()->GetParent()); + if (inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH || + mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto || + mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) { computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight); + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); } } else { - MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, - "We're not in a flex container, so the flag " - "'mIsFlexContainerMeasuringHeight' shouldn't be set"); + // Make sure legend frames with display:block and width:auto still + // shrink-wrap. + // Also shrink-wrap blocks that are orthogonal to their container. + if (isBlock && + ((aFrameType == nsGkAtoms::legendFrame && + frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || + (aFrameType == nsGkAtoms::scrollFrame && + frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) || + (mCBReflowState && + mCBReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)))) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + } - if (parentFrameType == nsGkAtoms::gridContainerFrame) { - auto justifySelf = mStylePosition->ComputedJustifySelf(mStyleDisplay, - frame->StyleContext()->GetParent()); - if (justifySelf != NS_STYLE_JUSTIFY_STRETCH) { + if (parentFrameType == nsGkAtoms::flexContainerFrame) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + + // If we're inside of a flex container that needs to measure our + // auto height, pass that information along to ComputeSize(). + if (mFlags.mIsFlexContainerMeasuringHeight) { computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight); } + } else { + MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, + "We're not in a flex container, so the flag " + "'mIsFlexContainerMeasuringHeight' shouldn't be set"); } } From d9b22006aad33b5380d4986af04993762d560b71 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 043/102] Bug 1176793 part 2 - [css-grid] Reftests for margin:auto on grid items. --- .../css-grid/grid-abspos-items-012-ref.html | 45 +++++++ .../css-grid/grid-abspos-items-012.html | 47 +++++++ .../grid-item-margin-left-auto-001-ref.html | 117 +++++++++++++++++ .../grid-item-margin-left-auto-001.html | 116 +++++++++++++++++ .../grid-item-margin-left-auto-002-ref.html | 115 +++++++++++++++++ .../grid-item-margin-left-auto-002.html | 116 +++++++++++++++++ .../grid-item-margin-left-auto-003-ref.html | 116 +++++++++++++++++ .../grid-item-margin-left-auto-003.html | 115 +++++++++++++++++ .../grid-item-margin-left-auto-004-ref.html | 117 +++++++++++++++++ .../grid-item-margin-left-auto-004.html | 116 +++++++++++++++++ ...d-item-margin-left-right-auto-001-ref.html | 117 +++++++++++++++++ .../grid-item-margin-left-right-auto-001.html | 117 +++++++++++++++++ ...d-item-margin-left-right-auto-002-ref.html | 115 +++++++++++++++++ .../grid-item-margin-left-right-auto-002.html | 117 +++++++++++++++++ ...d-item-margin-left-right-auto-003-ref.html | 116 +++++++++++++++++ .../grid-item-margin-left-right-auto-003.html | 116 +++++++++++++++++ ...d-item-margin-left-right-auto-004-ref.html | 115 +++++++++++++++++ .../grid-item-margin-left-right-auto-004.html | 117 +++++++++++++++++ .../grid-item-margin-right-auto-001-ref.html | 117 +++++++++++++++++ .../grid-item-margin-right-auto-001.html | 116 +++++++++++++++++ .../grid-item-margin-right-auto-002-ref.html | 118 ++++++++++++++++++ .../grid-item-margin-right-auto-002.html | 116 +++++++++++++++++ .../grid-item-margin-right-auto-003-ref.html | 116 +++++++++++++++++ .../grid-item-margin-right-auto-003.html | 115 +++++++++++++++++ .../grid-item-margin-right-auto-004-ref.html | 117 +++++++++++++++++ .../grid-item-margin-right-auto-004.html | 116 +++++++++++++++++ layout/reftests/css-grid/reftest.list | 13 ++ 27 files changed, 2894 insertions(+) create mode 100644 layout/reftests/css-grid/grid-abspos-items-012-ref.html create mode 100644 layout/reftests/css-grid/grid-abspos-items-012.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-004.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-004.html diff --git a/layout/reftests/css-grid/grid-abspos-items-012-ref.html b/layout/reftests/css-grid/grid-abspos-items-012-ref.html new file mode 100644 index 000000000000..e0feef20518b --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-012-ref.html @@ -0,0 +1,45 @@ + + + + + Reference: margin:auto on grid abs.pos. child + + + + + +
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/css-grid/grid-abspos-items-012.html b/layout/reftests/css-grid/grid-abspos-items-012.html new file mode 100644 index 000000000000..69c96640c1c8 --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-012.html @@ -0,0 +1,47 @@ + + + + + CSS Grid Test: margin:auto on grid abs.pos. child + + + + + + + +
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html new file mode 100644 index 000000000000..a7892746df82 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-001.html b/layout/reftests/css-grid/grid-item-margin-left-auto-001.html new file mode 100644 index 000000000000..663f0ddec76a --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-001.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html new file mode 100644 index 000000000000..6319ef310be4 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-002.html b/layout/reftests/css-grid/grid-item-margin-left-auto-002.html new file mode 100644 index 000000000000..e68ede013250 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-002.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html new file mode 100644 index 000000000000..c7f197927998 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-left:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-003.html b/layout/reftests/css-grid/grid-item-margin-left-auto-003.html new file mode 100644 index 000000000000..353f8f02e08f --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-003.html @@ -0,0 +1,115 @@ + + + + + CSS Grid Test: margin-left:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html new file mode 100644 index 000000000000..4501f52fbc66 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-004.html b/layout/reftests/css-grid/grid-item-margin-left-auto-004.html new file mode 100644 index 000000000000..3eef662a4320 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-004.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html new file mode 100644 index 000000000000..b48f2e71565c --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left/right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html new file mode 100644 index 000000000000..fb198f796f0c --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html new file mode 100644 index 000000000000..dd2006d96c2b --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left/right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html new file mode 100644 index 000000000000..3dc049dbe92e --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html new file mode 100644 index 000000000000..7391f9e0fcbf --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-left/right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html new file mode 100644 index 000000000000..4de40bbcc5a5 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left/right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html new file mode 100644 index 000000000000..7463065f83c3 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left/right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html new file mode 100644 index 000000000000..b4799ab2fc97 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html new file mode 100644 index 000000000000..bfdb07aba090 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-001.html b/layout/reftests/css-grid/grid-item-margin-right-auto-001.html new file mode 100644 index 000000000000..6803478ba709 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-001.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html new file mode 100644 index 000000000000..4b2bd9788bc7 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html @@ -0,0 +1,118 @@ + + + + + Reference: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-002.html b/layout/reftests/css-grid/grid-item-margin-right-auto-002.html new file mode 100644 index 000000000000..839ffd83992e --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-002.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html new file mode 100644 index 000000000000..08a01afca454 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-003.html b/layout/reftests/css-grid/grid-item-margin-right-auto-003.html new file mode 100644 index 000000000000..ccec8f7dab59 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-003.html @@ -0,0 +1,115 @@ + + + + + CSS Grid Test: margin-right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html new file mode 100644 index 000000000000..d8efb6a018b1 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-004.html b/layout/reftests/css-grid/grid-item-margin-right-auto-004.html new file mode 100644 index 000000000000..fc3a2973b51e --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-004.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index bdf337780c7e..58c4626633a2 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -25,6 +25,7 @@ fails == grid-whitespace-handling-1b.xhtml grid-whitespace-handling-1-ref.xhtml == grid-abspos-items-009.html grid-abspos-items-009-ref.html == grid-abspos-items-010.html grid-abspos-items-010-ref.html == grid-abspos-items-011.html grid-abspos-items-011-ref.html +== grid-abspos-items-012.html grid-abspos-items-012-ref.html == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html @@ -79,3 +80,15 @@ fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min == grid-row-gap-003.html grid-row-gap-003-ref.html == grid-row-gap-004.html grid-row-gap-004-ref.html == grid-container-overflow-001.html grid-container-overflow-001-ref.html +== grid-item-margin-left-auto-001.html grid-item-margin-left-auto-001-ref.html +== grid-item-margin-left-auto-002.html grid-item-margin-left-auto-002-ref.html +== grid-item-margin-left-auto-003.html grid-item-margin-left-auto-003-ref.html +== grid-item-margin-left-auto-004.html grid-item-margin-left-auto-004-ref.html +== grid-item-margin-left-right-auto-001.html grid-item-margin-left-right-auto-001-ref.html +== grid-item-margin-left-right-auto-002.html grid-item-margin-left-right-auto-002-ref.html +== grid-item-margin-left-right-auto-003.html grid-item-margin-left-right-auto-003-ref.html +== grid-item-margin-left-right-auto-004.html grid-item-margin-left-right-auto-004-ref.html +== grid-item-margin-right-auto-001.html grid-item-margin-right-auto-001-ref.html +== grid-item-margin-right-auto-002.html grid-item-margin-right-auto-002-ref.html +== grid-item-margin-right-auto-003.html grid-item-margin-right-auto-003-ref.html +== grid-item-margin-right-auto-004.html grid-item-margin-right-auto-004-ref.html From 1998e3c898881b7f4e08e45edc6c5735bb7c9f39 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 044/102] Bug 1229145 - [css-grid] Account for start margin when calculating border box offset for align-self/justify-self:center. r=dholbert --- layout/generic/nsGridContainerFrame.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 6c2e01e892c6..123091a4bc8e 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -859,6 +859,7 @@ SpaceToFill(WritingMode aWM, const LogicalSize& aSize, nscoord aMargin, return aCBSize - (size + aMargin); } +// Align (or stretch) an item's margin box in its aAxis inside aCBSize. static bool AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, bool aSameSide, nscoord aCBSize, const nsHTMLReflowState& aRS, @@ -949,7 +950,7 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, // Set the position and size (aPos/aContentSize) for the requested alignment. bool didResize = false; - nscoord offset = 0; + nscoord offset = 0; // NOTE: this is the resulting frame offset (border box). switch (aAlignment) { case NS_STYLE_ALIGN_BASELINE: case NS_STYLE_ALIGN_LAST_BASELINE: @@ -963,10 +964,12 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, offset = aCBSize - (size + marginEnd); break; } - case NS_STYLE_ALIGN_CENTER: - offset = SpaceToFill(wm, aChildSize, marginStart + marginEnd, - aAxis, aCBSize) / 2; + case NS_STYLE_ALIGN_CENTER: { + nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm) + : aChildSize.ISize(wm); + offset = (aCBSize - size + marginStart - marginEnd) / 2; break; + } case NS_STYLE_ALIGN_STRETCH: { MOZ_ASSERT(!hasAutoMarginStart && !hasAutoMarginEnd); offset = marginStart; From 23d87b84ccf084c7b75a767717137bb786535c3b Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 1 Dec 2015 23:20:18 +0100 Subject: [PATCH 045/102] Bug 1229145 - [css-grid] Adjust align-self/justify-self:center reference rendering to reflect the corrected margin calculations. --- .../css-grid/grid-item-align-001-ref.html | 30 +++++++++---------- .../css-grid/grid-item-align-002-ref.html | 4 +-- .../css-grid/grid-item-align-003-ref.html | 4 +-- .../css-grid/grid-item-justify-001-ref.html | 6 ++-- .../css-grid/grid-item-justify-002-ref.html | 4 +-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/layout/reftests/css-grid/grid-item-align-001-ref.html b/layout/reftests/css-grid/grid-item-align-001-ref.html index 6525b6f62e58..35bb263ac730 100644 --- a/layout/reftests/css-grid/grid-item-align-001-ref.html +++ b/layout/reftests/css-grid/grid-item-align-001-ref.html @@ -67,7 +67,7 @@ abs4 { display:none; } } .aend,.aflexend { offset-block-start: 9px; } -.acenter { offset-block-start: 5px; } +.acenter { offset-block-start: 5px; margin-block-start:2px; } .hl .astretch2, .hr .astretch2 { height: 15px; } .hl .astretch3, .hr .astretch3 { height: 15px; } @@ -82,55 +82,55 @@ abs4 { display:none; } .hl .hr {margin-left:4px;} .hl .vl {offset-block-start: 1px; offset-inline-start:3px;} .hl .vl.aend, .hl .vl.aflexend { margin-top: 7px; } -.hl .vl.acenter { margin-top:3px; } +.hl .vl.acenter { margin-top:4px; } .hl .vr {margin-left:6px; margin-top:3px; } .hl .vr.aend, .hl .vr.aflexend {margin-left:12px; margin-top:9px; } -.hl .vr.acenter {margin-left:8px; margin-top:5px; } +.hl .vr.acenter {margin-left:8px; margin-top:6px; } .hl .vlr { margin-left:0px; margin-top:5px; } .hl .vlr.aend, .hl .vlr.aflexend {margin-left:-6px; margin-top:11px; } -.hl .vlr.acenter {margin-left:-2px; margin-top:7px; } +.hl .vlr.acenter {margin-left:-2px; margin-top:8px; } .hl .vrl { margin-left:6px; margin-top:3px; } .hl .vrl.aend, .hl .vrl.aflexend {margin-left:12px; margin-top:9px; } -.hl .vrl.acenter {margin-left:8px; margin-top:5px; } +.hl .vrl.acenter {margin-left:8px; margin-top:6px; } .hr .hl {margin-right:3px} .hr .vl {margin-right:5px; margin-top:5px; } .hr .vl.aend, .hr .vl.aflexend {margin-right:11px; margin-top:11px; } -.hr .vl.acenter {margin-right:7px; margin-top:7px; } +.hr .vl.acenter {margin-right:7px; margin-top:8px; } .hr .vr {margin-top:5px; margin-right:-1px; } .hr .vr.aend, .hr .vr.aflexend {margin-top:11px; margin-right:-7px; } -.hr .vr.acenter {margin-top:7px; margin-right:-3px; } +.hr .vr.acenter {margin-top:8px; margin-right:-3px; } .hr .vr.astretch6, .hr .vr.astretch7 { margin-right:-1px; } .hr .vlr {margin-top:5px; margin-right:5px; } .hr .vlr.aend, .hr .vlr.aflexend {margin-top:11px; margin-right:11px; } -.hr .vlr.acenter {margin-top:7px; margin-right:7px; } +.hr .vlr.acenter {margin-top:8px; margin-right:7px; } .hr .vrl {margin-top:3px; margin-right:-1px; } .hr .vrl.aend, .hr .vrl.aflexend {margin-top:9px; margin-right:-7px; } -.hr .vrl.acenter {margin-top:5px; margin-right:-3px; } +.hr .vrl.acenter {margin-top:6px; margin-right:-3px; } .vl span { offset-block-start: 1px; offset-inline-start: 3px; } .vl .astretch4 { width:15px; } .vl .astretch5 { width:13px; } .vl .hl.aend, .vl .hl.aflexend { margin-left:4px; } -.vl .hl.acenter { margin-left:1px; } +.vl .hl.acenter { margin-left:3px; margin-top:1px; } .vl .astretch6, .vl .astretch7 { height:0px; } .vl .hr.astretch6, .vl .hr.astretch7 { height:0px; } .vl .hr { margin-left:8px; } .vl .hr.aend, .vl .hr.aflexend { margin-left:10px; } -.vl .hr.acenter { margin-left:7px; } +.vl .hr.acenter { margin-left:9px; margin-top:1px; } .vl .vl { margin-top:-1px; margin-left:4px; } .vl .vl.aend, .vl .vl.aflexend { margin-left:6px; } -.vl .vl.acenter { margin-left:3px; } +.vl .vl.acenter { margin-left:5px; margin-top:-1px; } .vl .astretch7 { width:13px; } .vl .vl.astretch2, .vl .vl.astretch3, @@ -140,15 +140,15 @@ abs4 { display:none; } .vl .vr { margin-top:-1px; margin-left:6px; } .vl .vr.aend, .vl .vr.aflexend { margin-left:8px; } -.vl .vr.acenter { margin-left:5px; } +.vl .vr.acenter { margin-left:7px; margin-top:-1px; } .vl .vlr { margin-top:5px; margin-left:4px; } .vl .vlr.aend, .vl .vlr.aflexend { margin-left:6px; } -.vl .vlr.acenter { margin-left:3px; } +.vl .vlr.acenter { margin-left:5px; } .vl .vrl { margin-top:-1px; margin-left:6px; } .vl .vrl.aend, .vl .vrl.aflexend { margin-left:8px; } -.vl .vrl.acenter { margin-left:5px; } +.vl .vrl.acenter { margin-left:7px; } diff --git a/layout/reftests/css-grid/grid-item-align-002-ref.html b/layout/reftests/css-grid/grid-item-align-002-ref.html index 3a52f7f630be..73d60d4c2092 100644 --- a/layout/reftests/css-grid/grid-item-align-002-ref.html +++ b/layout/reftests/css-grid/grid-item-align-002-ref.html @@ -69,7 +69,7 @@ abs4 { grid-area: 3 / 1 / 4 / 4; } .aflexend { align-self:flex-end; } .aselfstart { align-self:self-start; } .aselfend { align-self:self-end; } -.acenter { align-self:center; } +.acenter { margin-right:2px; } .aleft { align-self:left; } .aright { align-self:right; } .astretch1 { align-self:stretch; } @@ -83,7 +83,7 @@ abs4 { grid-area: 3 / 1 / 4 / 4; } .vr .aend, .vr .aflexend { margin-right:3px; } .vlr .aend, .vlr .aflexend { margin-left:4px; } -.vlr .acenter { margin-left:1px; } +.vlr .acenter { margin-left:3px; } .vrl .aend, .vrl .aflexend { margin-right:3px; } .astretch6 { width:13px; } diff --git a/layout/reftests/css-grid/grid-item-align-003-ref.html b/layout/reftests/css-grid/grid-item-align-003-ref.html index c4db0670eae8..3ab6ee32982c 100644 --- a/layout/reftests/css-grid/grid-item-align-003-ref.html +++ b/layout/reftests/css-grid/grid-item-align-003-ref.html @@ -67,8 +67,8 @@ abs4 { right: 35px; } .true.hl.aend, .true.hl.aflexend { offset-block-start:-15px; } .true.vrl.aend, .true.vrl.aflexend { offset-inline-start:-15px; } -.true.hl.acenter { offset-block-start:-8px; } -.true.vrl.acenter { offset-inline-start:-8px; } +.true.hl.acenter { offset-block-start:-7px; } +.true.vrl.acenter { offset-inline-start:-7px; } .astretch2 { width:40px; height:15px; } .astretch3 { height:15px; } .astretch4 { width:0; } diff --git a/layout/reftests/css-grid/grid-item-justify-001-ref.html b/layout/reftests/css-grid/grid-item-justify-001-ref.html index 3af8fba42def..99c8536454e8 100644 --- a/layout/reftests/css-grid/grid-item-justify-001-ref.html +++ b/layout/reftests/css-grid/grid-item-justify-001-ref.html @@ -74,9 +74,9 @@ span { .hl .jend, .hl .jflexend, .hl .jright { margin-left: 16px; } .hr .jend, .hr .jflexend { margin-right: 15px; } -.hl .jcenter { margin-left: 7px; } +.hl .jcenter { margin-left: 9px; } .vl .jend, .vl .jflexend { margin-right:3px; } -.hr .jcenter { margin-right: 7px; } +.hr .jcenter { margin-right: 8px; } .hr .jleft { margin-right: 15px; } .hl .jstretch5, .hl .jstretch7, .hr .jstretch5, .hr .jstretch7 { width:14px; } @@ -87,7 +87,7 @@ span { .vl .jstretch2, .vl .jstretch3 { height:27px; } .vl .jstretch6, .vl .jstretch7 { height:9px; } .vl .jend, .vl .jflexend, .vl .jright { margin-top: 19px; } -.vl .jcenter { margin-top: 9px; } +.vl .jcenter { margin-top: 10px; } diff --git a/layout/reftests/css-grid/grid-item-justify-002-ref.html b/layout/reftests/css-grid/grid-item-justify-002-ref.html index 2d0f1e75da67..39e7d26f0e1c 100644 --- a/layout/reftests/css-grid/grid-item-justify-002-ref.html +++ b/layout/reftests/css-grid/grid-item-justify-002-ref.html @@ -85,8 +85,8 @@ abs4 { grid-area: 3 / 1 / 4 / 4; } .vr .jend, .vr .jflexend, .vr .jright, .vrl .jend, .vrl .jflexend, .vrl .jright { margin-top: 19px; } .vlr .jend, .vlr .jflexend, .vlr .jleft { margin-bottom: 20px;} -.vr .jcenter, .vrl .jcenter { margin-top: 9px; } -.vlr .jcenter { margin-bottom: 9px; } +.vr .jcenter, .vrl .jcenter { margin-top: 10px; } +.vlr .jcenter { margin-bottom: 11px; } From d292922eee6844052ff7839c5ed7f38235e2fd00 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:12 -0800 Subject: [PATCH 046/102] Bug 1214812 - [mozdevice] - devicemanagerADB.py - use su 0 if it is available; check for root updates, r=gbrown. --- .../mozdevice/mozdevice/devicemanagerADB.py | 65 ++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index 8b3bddcff801..708f99e796df 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -23,8 +23,9 @@ class DeviceManagerADB(DeviceManager): port. """ - _haveRootShell = False - _haveSu = False + _haveRootShell = None + _haveSu = None + _suModifier = None _useZip = False _logcatNeedsRoot = False _pollingInterval = 0.01 @@ -101,17 +102,22 @@ class DeviceManagerADB(DeviceManager): # always. :( # If requested to run as root, check that we can actually do that - if root and not self._haveRootShell and not self._haveSu: - raise DMError("Shell command '%s' requested to run as root but root " - "is not available on this device. Root your device or " - "refactor the test/harness to not require root." % - self._escapedCommandLine(cmd)) + if root: + if self._haveRootShell is None and self._haveSu is None: + self._checkForRoot() + if not self._haveRootShell and not self._haveSu: + raise DMError( + "Shell command '%s' requested to run as root but root " + "is not available on this device. Root your device or " + "refactor the test/harness to not require root." % + self._escapedCommandLine(cmd)) # Getting the return code is more complex than you'd think because adb # doesn't actually return the return code from a process, so we have to # capture the output to get it - if root and not self._haveRootShell: - cmdline = "su -c \"%s\"" % self._escapedCommandLine(cmd) + if root and self._haveSu: + cmdline = "su %s \"%s\"" % (self._suModifier, + self._escapedCommandLine(cmd)) else: cmdline = self._escapedCommandLine(cmd) cmdline += "; echo $?" @@ -662,6 +668,13 @@ class DeviceManagerADB(DeviceManager): raise DMError("unable to connect to device") def _checkForRoot(self): + self._haveRootShell = False + self._haveSu = False + # If requested to attempt to run adbd as root, do so before + # checking whether adbs is running as root. + if self._runAdbAsRoot: + self._adb_root() + # Check whether we _are_ root by default (some development boards work # this way, this is also the result of some relatively rare rooting # techniques) @@ -673,22 +686,30 @@ class DeviceManagerADB(DeviceManager): # if root shell is not available, check if 'su' can be used to gain # root - proc = self._runCmd(["shell", "su", "-c", "id"], timeout=self.short_timeout) + def su_id(su_modifier, timeout): + proc = self._runCmd(["shell", "su", su_modifier, "id"], + timeout=timeout) - # wait for response for maximum of 15 seconds, in case su prompts for a - # password or triggers the Android SuperUser prompt - start_time = time.time() - retcode = None - while (time.time() - start_time) <= 15 and retcode is None: - retcode = proc.poll() - if retcode is None: # still not terminated, kill - proc.kill() + # wait for response for maximum of 15 seconds, in case su + # prompts for a password or triggers the Android SuperUser + # prompt + start_time = time.time() + retcode = None + while (time.time() - start_time) <= 15 and retcode is None: + retcode = proc.poll() + if retcode is None: # still not terminated, kill + proc.kill() - if proc.output and 'uid=0(root)' in proc.output[0]: + if proc.output and 'uid=0(root)' in proc.output[0]: + return True + return False + + if su_id('0', self.short_timeout): self._haveSu = True - - if self._runAdbAsRoot: - self._adb_root() + self._suModifier = '0' + elif su_id('-c', self.short_timeout): + self._haveSu = True + self._suModifier = '-c' def _isUnzipAvailable(self): data = self._runCmd(["shell", "unzip"]).output From da4e38e6e718efa9acb6586407db70a2297f86aa Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:12 -0800 Subject: [PATCH 047/102] Bug 1214812 - [mozdevice] - devicemanagerADB.py - check if busybox's ls -1A is used, r=gbrown. --- .../mozdevice/mozdevice/devicemanagerADB.py | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index 708f99e796df..e9e5d11d0c43 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -26,6 +26,7 @@ class DeviceManagerADB(DeviceManager): _haveRootShell = None _haveSu = None _suModifier = None + _lsModifier = None _useZip = False _logcatNeedsRoot = False _pollingInterval = 0.01 @@ -71,6 +72,8 @@ class DeviceManagerADB(DeviceManager): if autoconnect: self.connect() + self._detectLsModifier() + def connect(self): if not self.connected: # try to connect to the device over tcp/ip if we have a hostname @@ -265,7 +268,8 @@ class DeviceManagerADB(DeviceManager): mozfile.remove(tmpDir) def dirExists(self, remotePath): - data = self._runCmd(["shell", "ls", "-a", remotePath + '/'], timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", self._lsModifier, remotePath + '/'], + timeout=self.short_timeout).output if len(data) == 1: res = data[0] @@ -274,7 +278,8 @@ class DeviceManagerADB(DeviceManager): return True def fileExists(self, filepath): - data = self._runCmd(["shell", "ls", "-a", filepath], timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", self._lsModifier, filepath], + timeout=self.short_timeout).output if len(data) == 1: foundpath = data[0].decode('utf-8').rstrip() if foundpath == filepath: @@ -298,7 +303,8 @@ class DeviceManagerADB(DeviceManager): self._checkCmd(["shell", "dd", "if=%s" % source, "of=%s" % destination]) def listFiles(self, rootdir): - data = self._runCmd(["shell", "ls", "-a", rootdir], timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", self._lsModifier, rootdir], + timeout=self.short_timeout).output data[:] = [item.rstrip('\r\n') for item in data] if (len(data) == 1): if (data[0] == rootdir): @@ -750,3 +756,18 @@ class DeviceManagerADB(DeviceManager): self._checkCmd(["wait-for-device"]) if self.processInfo("adbd")[2] != "root": raise DMError("We tried rebooting adbd as root, however, it failed.") + + def _detectLsModifier(self): + if self._lsModifier is None: + # Check if busybox -1A is required in order to get one + # file per line. + output = self._runCmd(["shell", "ls", "-1A" "/"], + timeout=self.short_timeout).output + output = ' '.join(output) + if "Unknown option '-1'. Aborting." in output: + self._lsModifier = "-a" + elif "-1A/: No such file or directory" in output: + self._lsModifier = "-a" + else: + self._lsModifier = "-1A" + From 51794b516293091e71ab3ce6d1c70ad877f7b4bb Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:12 -0800 Subject: [PATCH 048/102] Bug 1214812 - [mozdevice] - devicemanagerADB.py - listFiles - check for device or resource busy failure message, r=gbrown. --- testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index e9e5d11d0c43..35d6d8239ce9 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -317,6 +317,8 @@ class DeviceManagerADB(DeviceManager): return [] if (data[0].find("opendir failed") != -1): return [] + if (data[0].find("Device or resource busy") != -1): + return [] return data def getProcessList(self): From d29912ed0ed1d7b21316d948b46f212a8f2bd708 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:12 -0800 Subject: [PATCH 049/102] Bug 1214812 - [mozdevice] - adb.py - do not prepend LD_LIBRARY_PATH to su commands, r=gbrown. --- testing/mozbase/mozdevice/mozdevice/adb.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/adb.py b/testing/mozbase/mozdevice/mozdevice/adb.py index 0e93db3a5e05..d5eba383ca2a 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -536,7 +536,6 @@ class ADBDevice(ADBCommand): self._have_android_su = False uid = 'uid=0' - cmd_id = 'LD_LIBRARY_PATH=/vendor/lib:/system/lib id' # Is shell already running as root? # Catch exceptions due to the potential for segfaults # calling su when using an improperly rooted device. @@ -548,7 +547,7 @@ class ADBDevice(ADBCommand): # Do we have a 'Superuser' sh like su? try: - if self.shell_output("su -c '%s'" % cmd_id).find(uid) != -1: + if self.shell_output("su -c id").find(uid) != -1: self._have_su = True except ADBError: self._logger.debug("Check for su failed") @@ -953,15 +952,15 @@ class ADBDevice(ADBCommand): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. """ - if root: - ld_library_path='LD_LIBRARY_PATH=/vendor/lib:/system/lib' - cmd = '%s %s' % (ld_library_path, cmd) - if self._have_root_shell: - pass + if root and not self._have_root_shell: + # If root was requested and we do not already have a root + # shell, then use the appropriate version of su to invoke + # the shell cmd. Prefer Android's su version since it may + # falsely report support for su -c. + if self._have_android_su: + cmd = "su 0 %s" % cmd elif self._have_su: cmd = "su -c \"%s\"" % cmd - elif self._have_android_su: - cmd = "su 0 \"%s\"" % cmd else: raise ADBRootError('Can not run command %s as root!' % cmd) From f0fb183222a4db7c3c32606bf5dde226231a92a5 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:12 -0800 Subject: [PATCH 050/102] Bug 1214812 - [mozdevice] - adb.py, adb_android.py - if possible always run adbd as root, set SELinux to Permissive, r=gbrown. --- testing/mozbase/mozdevice/mozdevice/adb.py | 65 ++++++++--- .../mozdevice/mozdevice/adb_android.py | 108 +++++++++++++++++- 2 files changed, 153 insertions(+), 20 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/adb.py b/testing/mozbase/mozdevice/mozdevice/adb.py index d5eba383ca2a..a437f51b5286 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -2,7 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. -from abc import ABCMeta, abstractmethod import os import posixpath import re @@ -11,6 +10,8 @@ import tempfile import time import traceback +from abc import ABCMeta, abstractmethod + class ADBProcess(object): """ADBProcess encapsulates the data related to executing the adb process.""" @@ -535,49 +536,58 @@ class ADBDevice(ADBCommand): self._have_su = False self._have_android_su = False - uid = 'uid=0' - # Is shell already running as root? # Catch exceptions due to the potential for segfaults # calling su when using an improperly rooted device. - try: - if self.shell_output("id").find(uid) != -1: - self._have_root_shell = True - except ADBError: - self._logger.debug("Check for root shell failed") + # Note this check to see if adbd is running is performed on + # the device in the state it exists in when the ADBDevice is + # initialized. It may be the case that it has been manipulated + # since its last boot and that its current state does not + # match the state the device will have immediately after a + # reboot. For example, if adb root was called manually prior + # to ADBDevice being initialized, then self._have_root_shell + # will not reflect the state of the device after it has been + # rebooted again. Therefore this check will need to be + # performed again after a reboot. + + self._check_adb_root(timeout=timeout) + + uid = 'uid=0' # Do we have a 'Superuser' sh like su? try: - if self.shell_output("su -c id").find(uid) != -1: + if self.shell_output("su -c id", timeout=timeout).find(uid) != -1: self._have_su = True + self._logger.info("su -c supported") except ADBError: - self._logger.debug("Check for su failed") + self._logger.debug("Check for su -c failed") # Do we have Android's su? try: - if self.shell_output("su 0 id").find(uid) != -1: + if self.shell_output("su 0 id", timeout=timeout).find(uid) != -1: self._have_android_su = True + self._logger.info("su 0 supported") except ADBError: - self._logger.debug("Check for Android su failed") + self._logger.debug("Check for su 0 failed") self._mkdir_p = None # Force the use of /system/bin/ls or /system/xbin/ls in case # there is /sbin/ls which embeds ansi escape codes to colorize # the output. Detect if we are using busybox ls. We want each # entry on a single line and we don't want . or .. - if self.shell_bool("/system/bin/ls /"): + if self.shell_bool("/system/bin/ls /", timeout=timeout): self._ls = "/system/bin/ls" - elif self.shell_bool("/system/xbin/ls /"): + elif self.shell_bool("/system/xbin/ls /", timeout=timeout): self._ls = "/system/xbin/ls" else: raise ADBError("ADBDevice.__init__: ls not found") try: - self.shell_output("%s -1A /" % self._ls) + self.shell_output("%s -1A /" % self._ls, timeout=timeout) self._ls += " -1A" except ADBError: self._ls += " -a" # Do we have cp? - self._have_cp = self.shell_bool("type cp") + self._have_cp = self.shell_bool("type cp", timeout=timeout) self._logger.debug("ADBDevice: %s" % self.__dict__) @@ -614,6 +624,28 @@ class ADBDevice(ADBCommand): raise ValueError("Unable to get device serial") + def _check_adb_root(self, timeout=None): + self._have_root_shell = False + uid = 'uid=0' + # Is shell already running as root? + try: + if self.shell_output("id", timeout=timeout).find(uid) != -1: + self._have_root_shell = True + self._logger.info("adbd running as root") + except ADBError: + self._logger.debug("Check for root shell failed") + + # Do we need to run adb root to get a root shell? + try: + if (not self._have_root_shell and + self.command_output( + ["root"], + timeout=timeout).find("cannot run as root") == -1): + self._have_root_shell = True + self._logger.info("adbd restarted as root") + except ADBError: + self._logger.debug("Check for root adbd failed") + @staticmethod def _escape_command_line(cmd): @@ -1859,6 +1891,7 @@ class ADBDevice(ADBCommand): # 'wait-for-device' arguments which is an error in newer # versions of adb. self.command_output([], timeout=timeout) + self._check_adb_root(timeout=timeout) return self.is_device_ready(timeout=timeout) @abstractmethod diff --git a/testing/mozbase/mozdevice/mozdevice/adb_android.py b/testing/mozbase/mozdevice/mozdevice/adb_android.py index 29f323bbb904..7f426134377f 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb_android.py +++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py @@ -6,6 +6,8 @@ import os import re import time +from abc import ABCMeta + import version_codes from adb import ADBDevice, ADBError @@ -24,6 +26,99 @@ class ADBAndroid(ADBDevice): if adbdevice.process_exist("org.mozilla.fennec"): print "Fennec is running" """ + __metaclass__ = ABCMeta + + def __init__(self, + device=None, + adb='adb', + adb_host=None, + adb_port=None, + test_root='', + logger_name='adb', + timeout=300, + verbose=False, + device_ready_retry_wait=20, + device_ready_retry_attempts=3): + """Initializes the ADBAndroid object. + + :param device: When a string is passed, it is interpreted as the + device serial number. This form is not compatible with + devices containing a ":" in the serial; in this case + ValueError will be raised. + When a dictionary is passed it must have one or both of + the keys "device_serial" and "usb". This is compatible + with the dictionaries in the list returned by + ADBHost.devices(). If the value of device_serial is a + valid serial not containing a ":" it will be used to + identify the device, otherwise the value of the usb key, + prefixed with "usb:" is used. + If None is passed and there is exactly one device attached + to the host, that device is used. If there is more than one + device attached, ValueError is raised. If no device is + attached the constructor will block until a device is + attached or the timeout is reached. + :type device: dict, str or None + :param adb_host: host of the adb server to connect to. + :type adb_host: str or None + :param adb_port: port of the adb server to connect to. + :type adb_port: integer or None + :param str logger_name: logging logger name. Defaults to 'adb'. + :param integer device_ready_retry_wait: number of seconds to wait + between attempts to check if the device is ready after a + reboot. + :param integer device_ready_retry_attempts: number of attempts when + checking if a device is ready. + + :raises: * ADBError + * ADBTimeoutError + * ValueError + """ + ADBDevice.__init__(self, device=device, adb=adb, + adb_host=adb_host, adb_port=adb_port, + logger_name=logger_name, timeout=timeout, + verbose=verbose, + device_ready_retry_wait=device_ready_retry_wait, + device_ready_retry_attempts=device_ready_retry_attempts) + # https://source.android.com/devices/tech/security/selinux/index.html + # setenforce + # usage: setenforce [ Enforcing | Permissive | 1 | 0 ] + # getenforce returns either Enforcing or Permissive + + try: + self.selinux = True + if self.shell_output('getenforce', timeout=timeout) != 'Permissive': + self._logger.info('Setting SELinux Permissive Mode') + self.shell_output("setenforce Permissive", timeout=timeout, root=True) + except ADBError: + self.selinux = False + + def reboot(self, timeout=None): + """Reboots the device. + + :param timeout: optional integer specifying the maximum time in + seconds for any spawned adb process to complete before + throwing an ADBTimeoutError. + This timeout is per adb call. The total time spent + may exceed this value. If it is not specified, the value + set in the ADB constructor is used. + :raises: * ADBTimeoutError + * ADBError + + reboot() reboots the device, issues an adb wait-for-device in order to + wait for the device to complete rebooting, then calls is_device_ready() + to determine if the device has completed booting. + + If the device supports running adbd as root, adbd will be + restarted running as root. Then, if the device supports + SELinux, setenforce Permissive will be called to change + SELinux to permissive. This must be done after adbd is + restarted in order for the SELinux Permissive setting to + persist. + + """ + ready = ADBDevice.reboot(self, timeout=timeout) + self._check_adb_root(timeout=timeout) + return ready # Informational methods @@ -99,10 +194,15 @@ class ADBAndroid(ADBDevice): failure = "Device state: %s" % state success = False else: - if self.is_dir(ready_path, timeout=timeout): - self.rmdir(ready_path, timeout=timeout) - self.mkdir(ready_path, timeout=timeout) - self.rmdir(ready_path, timeout=timeout) + if (self.selinux and + self.shell_output('getenforce', + timeout=timeout) != 'Permissive'): + self._logger.info('Setting SELinux Permissive Mode') + self.shell_output("setenforce Permissive", timeout=timeout, root=True) + if self.is_dir(ready_path, timeout=timeout, root=True): + self.rmdir(ready_path, timeout=timeout, root=True) + self.mkdir(ready_path, timeout=timeout, root=True) + self.rmdir(ready_path, timeout=timeout, root=True) # Invoke the pm list commands to see if it is up and # running. for pm_list_cmd in pm_list_commands: From 28f6dda48dfda06358c087beba2f964ec8cc98c9 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Tue, 1 Dec 2015 14:25:13 -0800 Subject: [PATCH 051/102] Bug 1214812 - [mozdevice] - adb_android.py - fix missing test_root argument to ADBAndroid, r=gbrown. --- testing/mozbase/mozdevice/mozdevice/adb_android.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/mozbase/mozdevice/mozdevice/adb_android.py b/testing/mozbase/mozdevice/mozdevice/adb_android.py index 7f426134377f..1b6d55af5375 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb_android.py +++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py @@ -75,6 +75,7 @@ class ADBAndroid(ADBDevice): """ ADBDevice.__init__(self, device=device, adb=adb, adb_host=adb_host, adb_port=adb_port, + test_root=test_root, logger_name=logger_name, timeout=timeout, verbose=verbose, device_ready_retry_wait=device_ready_retry_wait, From 9a273aba8df5abc39be34aef8e01c52556dd88c9 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Thu, 19 Nov 2015 16:37:36 +1300 Subject: [PATCH 052/102] Bug 1225703 - Update in-tree libcubeb. r=padenot --- media/libcubeb/AUTHORS | 2 + media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/include/cubeb.h | 109 +++++++ media/libcubeb/src/cubeb-internal.h | 5 +- media/libcubeb/src/cubeb.c | 60 +++- media/libcubeb/src/cubeb_alsa.c | 3 +- media/libcubeb/src/cubeb_audiotrack.c | 1 + media/libcubeb/src/cubeb_audiounit.c | 296 ++++++++++++++++- media/libcubeb/src/cubeb_opensl.c | 1 + media/libcubeb/src/cubeb_pulse.c | 214 ++++++++++++- media/libcubeb/src/cubeb_sndio.c | 1 + media/libcubeb/src/cubeb_wasapi.cpp | 439 ++++++++++++++++++++------ media/libcubeb/src/cubeb_winmm.c | 327 ++++++++++++++++++- media/libcubeb/tests/common.h | 1 + media/libcubeb/tests/moz.build | 2 + media/libcubeb/tests/test_audio.cpp | 32 +- media/libcubeb/tests/test_latency.cpp | 5 + media/libcubeb/tests/test_sanity.cpp | 119 +++---- media/libcubeb/tests/test_tone.cpp | 39 ++- 19 files changed, 1498 insertions(+), 160 deletions(-) diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS index b441e8a1c374..394e9ae6768c 100644 --- a/media/libcubeb/AUTHORS +++ b/media/libcubeb/AUTHORS @@ -4,3 +4,5 @@ Michael Wu Paul Adenot David Richards Sebastien Alaiwan +KO Myung-Hun +Haakon Sporsheim diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 6272a228c3f6..f105f5d7fbf2 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was 588c82be50ffee59b7fab71b56e6081a5a89301c. +The git commit ID used was eb30ecdf0c6b02e463a91ac93887ac08e31b360a. diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h index cd26dba7863b..4c016e0d231e 100644 --- a/media/libcubeb/include/cubeb.h +++ b/media/libcubeb/include/cubeb.h @@ -152,6 +152,74 @@ enum { CUBEB_ERROR_NOT_SUPPORTED = -4 /**< Optional function not implemented in current backend. */ }; +typedef enum { + CUBEB_DEVICE_TYPE_UNKNOWN, + CUBEB_DEVICE_TYPE_INPUT, + CUBEB_DEVICE_TYPE_OUTPUT +} cubeb_device_type; + +typedef enum { + CUBEB_DEVICE_STATE_DISABLED, + CUBEB_DEVICE_STATE_UNPLUGGED, + CUBEB_DEVICE_STATE_ENABLED +} cubeb_device_state; + +typedef void * cubeb_devid; + +typedef enum { + CUBEB_DEVICE_FMT_S16LE = 0x0010, + CUBEB_DEVICE_FMT_S16BE = 0x0020, + CUBEB_DEVICE_FMT_F32LE = 0x1000, + CUBEB_DEVICE_FMT_F32BE = 0x2000 +} cubeb_device_fmt; + +#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE +#else +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE +#endif +#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE) +#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE) +#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK) + +typedef enum { + CUBEB_DEVICE_PREF_NONE = 0x00, + CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01, + CUBEB_DEVICE_PREF_VOICE = 0x02, + CUBEB_DEVICE_PREF_NOTIFICATION = 0x04, + CUBEB_DEVICE_PREF_ALL = 0x0F +} cubeb_device_pref; + +typedef struct { + cubeb_devid devid; /* Device identifier handle */ + char * device_id; /* Device identifier which might be presented in a UI */ + char * friendly_name; /* Friendly device name which might be presented in a UI */ + char * group_id; /* Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ + char * vendor_name; /* Optional vendor name, may be NULL */ + + cubeb_device_type type; /* Type of device (Input/Output) */ + cubeb_device_state state; /* State of device disabled/enabled/unplugged */ + cubeb_device_pref preferred;/* Preferred device */ + + cubeb_device_fmt format; /* Sample format supported */ + cubeb_device_fmt default_format; + unsigned int max_channels; /* Channels */ + unsigned int default_rate; /* Default/Preferred sample rate */ + unsigned int max_rate; /* Maximum sample rate supported */ + unsigned int min_rate; /* Minimum sample rate supported */ + + unsigned int latency_lo_ms; /* Lowest possible latency in milliseconds */ + unsigned int latency_hi_ms; /* Higest possible latency in milliseconds */ +} cubeb_device_info; + +/** Device collection. */ +typedef struct { + uint32_t count; /**< Device count in collection. */ + cubeb_device_info * device[1]; /**< Array of pointers to device info. */ +} cubeb_device_collection; + /** User supplied data callback. @param stream @param user_ptr @@ -179,6 +247,12 @@ typedef void (* cubeb_state_callback)(cubeb_stream * stream, * @param user */ typedef void (* cubeb_device_changed_callback)(void * user_ptr); +/** + * User supplied callback called when the underlying device collection changed. + * @param context + * @param user_ptr */ +typedef void (* cubeb_device_collection_changed_callback)(cubeb * context, void * user_ptr); + /** Initialize an application context. This will perform any library or application scoped initialization. @param context @@ -337,6 +411,41 @@ int cubeb_stream_device_destroy(cubeb_stream * stream, int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, cubeb_device_changed_callback device_changed_callback); +/** Returns enumerated devices. + @param context + @param devtype device type to include + @param collection output collection. Must be destroyed with cubeb_device_collection_destroy + @retval CUBEB_OK in case of success + @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer + @retval CUBEB_ERROR_NOT_SUPPORTED */ +int cubeb_enumerate_devices(cubeb * context, + cubeb_device_type devtype, + cubeb_device_collection ** collection); + +/** Destroy a cubeb_device_collection. + @param collection collection to destroy + @retval CUBEB_OK + @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */ +int cubeb_device_collection_destroy(cubeb_device_collection * collection); + +/** Destroy a cubeb_device_info structure. + @param info pointer to device info structure + @retval CUBEB_OK + @retval CUBEB_ERROR_INVALID_PARAMETER if info is an invalid pointer */ +int cubeb_device_info_destroy(cubeb_device_info * info); + +/** Registers a callback which is called when the system detects + a new device or a device is removed. + @param context + @param callback a function called whenever the system device list changes. + Passing NULL allow to unregister a function + @param user_ptr pointer to user specified data which will be present in + subsequent callbacks. + @retval CUBEB_ERROR_NOT_SUPPORTED */ +int cubeb_register_device_collection_changed(cubeb * context, + cubeb_device_collection_changed_callback callback, + void * user_ptr); + #if defined(__cplusplus) } #endif diff --git a/media/libcubeb/src/cubeb-internal.h b/media/libcubeb/src/cubeb-internal.h index 503b59a469e1..323510712bbc 100644 --- a/media/libcubeb/src/cubeb-internal.h +++ b/media/libcubeb/src/cubeb-internal.h @@ -8,6 +8,8 @@ #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" +#include +#include struct cubeb_ops { int (* init)(cubeb ** context, char const * context_name); @@ -17,6 +19,8 @@ struct cubeb_ops { cubeb_stream_params params, uint32_t * latency_ms); int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate); + int (* enumerate_devices)(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection); void (* destroy)(cubeb * context); int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, @@ -36,7 +40,6 @@ struct cubeb_ops { cubeb_device * device); int (* stream_register_device_changed_callback)(cubeb_stream * stream, cubeb_device_changed_callback device_changed_callback); - }; #define XASSERT(expr) do { \ diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index 8f3b040a0bdb..f22e8a901892 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -7,6 +7,7 @@ #undef NDEBUG #include #include +#include #if defined(HAVE_CONFIG_H) #include "config.h" #endif @@ -56,6 +57,9 @@ int opensl_init(cubeb ** context, char const * context_name); #if defined(USE_AUDIOTRACK) int audiotrack_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_KAI) +int kai_init(cubeb ** context, char const * context_name); +#endif int validate_stream_params(cubeb_stream_params stream_params) @@ -89,12 +93,12 @@ int cubeb_init(cubeb ** context, char const * context_name) { int (* init[])(cubeb **, char const *) = { -#if defined(USE_PULSE) - pulse_init, -#endif #if defined(USE_JACK) jack_init, #endif +#if defined(USE_PULSE) + pulse_init, +#endif #if defined(USE_ALSA) alsa_init, #endif @@ -121,6 +125,9 @@ cubeb_init(cubeb ** context, char const * context_name) #endif #if defined(USE_AUDIOTRACK) audiotrack_init, +#endif +#if defined(USE_KAI) + kai_init, #endif }; int i; @@ -356,3 +363,50 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback); } + +int cubeb_enumerate_devices(cubeb * context, + cubeb_device_type devtype, + cubeb_device_collection ** collection) +{ + if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0) + return CUBEB_ERROR_INVALID_PARAMETER; + if (collection == NULL) + return CUBEB_ERROR_INVALID_PARAMETER; + if (!context->ops->enumerate_devices) + return CUBEB_ERROR_NOT_SUPPORTED; + + return context->ops->enumerate_devices(context, devtype, collection); +} + +int cubeb_device_collection_destroy(cubeb_device_collection * collection) +{ + uint32_t i; + + if (collection == NULL) + return CUBEB_ERROR_INVALID_PARAMETER; + + for (i = 0; i < collection->count; i++) + cubeb_device_info_destroy(collection->device[i]); + + free(collection); + return CUBEB_OK; +} + +int cubeb_device_info_destroy(cubeb_device_info * info) +{ + free(info->device_id); + free(info->friendly_name); + free(info->group_id); + free(info->vendor_name); + + free(info); + return CUBEB_OK; +} + +int cubeb_register_device_collection_changed(cubeb * context, + cubeb_device_collection_changed_callback callback, + void * user_ptr) +{ + return CUBEB_ERROR_NOT_SUPPORTED; +} + diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c index 5901a3b5bd8f..d9d26e7b20a1 100644 --- a/media/libcubeb/src/cubeb_alsa.c +++ b/media/libcubeb/src/cubeb_alsa.c @@ -963,7 +963,7 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { /* get a pcm, disabling resampling, so we get a rate the * hardware/dmix/pulse/etc. supports. */ - r = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); + r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); if (r < 0) { return CUBEB_ERROR; } @@ -1116,6 +1116,7 @@ static struct cubeb_ops const alsa_ops = { .get_max_channel_count = alsa_get_max_channel_count, .get_min_latency = alsa_get_min_latency, .get_preferred_sample_rate = alsa_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = alsa_destroy, .stream_init = alsa_stream_init, .stream_destroy = alsa_stream_destroy, diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c index cd6abe5334c9..bf85bb8c07c6 100644 --- a/media/libcubeb/src/cubeb_audiotrack.c +++ b/media/libcubeb/src/cubeb_audiotrack.c @@ -415,6 +415,7 @@ static struct cubeb_ops const audiotrack_ops = { .get_max_channel_count = audiotrack_get_max_channel_count, .get_min_latency = audiotrack_get_min_latency, .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = audiotrack_destroy, .stream_init = audiotrack_stream_init, .stream_destroy = audiotrack_stream_destroy, diff --git a/media/libcubeb/src/cubeb_audiounit.c b/media/libcubeb/src/cubeb_audiounit.c index 9b6b0e95a3bb..750610779216 100644 --- a/media/libcubeb/src/cubeb_audiounit.c +++ b/media/libcubeb/src/cubeb_audiounit.c @@ -159,7 +159,10 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags, pthread_mutex_unlock(&stm->mutex); if (stm->sample_spec.mChannelsPerFrame == 2) { - cubeb_pan_stereo_buffer_float((float*)buf, got, panning); + if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsFloat) + cubeb_pan_stereo_buffer_float((float*)buf, got, panning); + else if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsSignedInteger) + cubeb_pan_stereo_buffer_int((short*)buf, got, panning); } return noErr; @@ -400,6 +403,28 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) } #endif /* !TARGET_OS_IPHONE */ +static AudioObjectID +audiounit_get_default_device_id(cubeb_device_type type) +{ + AudioObjectPropertyAddress adr = { 0, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + AudioDeviceID devid; + UInt32 size; + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) + adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + else if (type == CUBEB_DEVICE_TYPE_INPUT) + adr.mSelector = kAudioHardwarePropertyDefaultInputDevice; + else + return kAudioObjectUnknown; + + size = sizeof(AudioDeviceID); + if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) { + return kAudioObjectUnknown; + } + + return devid; +} + int audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { @@ -996,12 +1021,281 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream, return CUBEB_OK; } +static OSStatus +audiounit_get_devices(AudioObjectID ** devices, uint32_t * count) +{ + OSStatus ret; + UInt32 size = 0; + AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + + ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size); + if (ret != noErr) + return ret; + + *count = (uint32_t)(size / sizeof(AudioObjectID)); + if (size >= sizeof(AudioObjectID)) { + if (*devices != NULL) free(*devices); + *devices = malloc(size); + memset(*devices, 0, size); + + ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices); + if (ret != noErr) { + free(*devices); + *devices = NULL; + } + } else { + *devices = NULL; + } + + return ret; +} + +static char * +audiounit_strref_to_cstr_utf8(CFStringRef strref) { + CFIndex len, size; + char * ret; + if (strref == NULL) + return NULL; + + len = CFStringGetLength(strref); + size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8); + ret = malloc(size); + + if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) { + free(ret); + ret = NULL; + } + + return ret; +} + +static uint32_t +audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + UInt32 size = 0; + uint32_t i, ret = 0; + + adr.mSelector = kAudioDevicePropertyStreamConfiguration; + + if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) { + AudioBufferList * list = alloca(size); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) { + for (i = 0; i < list->mNumberBuffers; i++) + ret += list->mBuffers[i].mNumberChannels; + } + } + + return ret; +} + +static void +audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope, + uint32_t * min, uint32_t * max, uint32_t * def) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + + adr.mSelector = kAudioDevicePropertyNominalSampleRate; + if (AudioObjectHasProperty(devid, &adr)) { + UInt32 size = sizeof(Float64); + Float64 fvalue = 0.0; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) + *def = fvalue; + } + + adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + if (AudioObjectHasProperty(devid, &adr)) { + UInt32 size = 0; + AudioValueRange range; + if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) { + uint32_t i, count = size / sizeof(AudioValueRange); + AudioValueRange * ranges = malloc(size); + range.mMinimum = 9999999999.0; + range.mMaximum = 0.0; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) { + for (i = 0; i < count; i++) { + if (ranges[i].mMaximum > range.mMaximum) + range.mMaximum = ranges[i].mMaximum; + if (ranges[i].mMinimum < range.mMinimum) + range.mMinimum = ranges[i].mMinimum; + } + } + free(ranges); + } + *max = (uint32_t)range.mMaximum; + *min = (uint32_t)range.mMinimum; + } else { + *min = *max = 0; + } + +} + +static UInt32 +audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + UInt32 size, dev, stream = 0, offset; + AudioStreamID sid[1]; + + adr.mSelector = kAudioDevicePropertyLatency; + size = sizeof(UInt32); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) + dev = 0; + + adr.mSelector = kAudioDevicePropertyStreams; + size = sizeof(sid); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) { + adr.mSelector = kAudioStreamPropertyLatency; + size = sizeof(UInt32); + AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream); + } + + adr.mSelector = kAudioDevicePropertySafetyOffset; + size = sizeof(UInt32); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) + offset = 0; + + return dev + stream + offset; +} + +static cubeb_device_info * +audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) +{ + AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster }; + UInt32 size, ch, latency; + cubeb_device_info * ret; + CFStringRef str = NULL; + AudioValueRange range; + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) { + adr.mScope = kAudioDevicePropertyScopeOutput; + } else if (type == CUBEB_DEVICE_TYPE_INPUT) { + adr.mScope = kAudioDevicePropertyScopeInput; + } else { + return NULL; + } + + if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0) + return NULL; + + ret = calloc(1, sizeof(cubeb_device_info)); + + size = sizeof(CFStringRef); + adr.mSelector = kAudioDevicePropertyDeviceUID; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + ret->device_id = audiounit_strref_to_cstr_utf8(str); + ret->devid = (cubeb_devid)ret->device_id; + ret->group_id = strdup(ret->device_id); + CFRelease(str); + } + + size = sizeof(CFStringRef); + adr.mSelector = kAudioObjectPropertyName; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + UInt32 ds; + size = sizeof(UInt32); + adr.mSelector = kAudioDevicePropertyDataSource; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) { + CFStringRef dsname; + AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) }; + adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; + size = sizeof(AudioValueTranslation); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) { + CFStringRef fullstr = CFStringCreateWithFormat(NULL, NULL, + CFSTR("%@ (%@)"), str, dsname); + CFRelease(dsname); + if (fullstr != NULL) { + CFRelease(str); + str = fullstr; + } + } + } + + ret->friendly_name = audiounit_strref_to_cstr_utf8(str); + CFRelease(str); + } + + size = sizeof(CFStringRef); + adr.mSelector = kAudioObjectPropertyManufacturer; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + ret->vendor_name = audiounit_strref_to_cstr_utf8(str); + CFRelease(str); + } + + ret->type = type; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = (devid == audiounit_get_default_device_id(type)) ? + CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + + ret->max_channels = ch; + ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ + /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */ + ret->default_format = CUBEB_DEVICE_FMT_F32NE; + audiounit_get_available_samplerate(devid, adr.mScope, + &ret->min_rate, &ret->max_rate, &ret->default_rate); + + latency = audiounit_get_device_presentation_latency(devid, adr.mScope); + + adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + size = sizeof(AudioValueRange); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) { + ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate; + ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate; + } else { + ret->latency_lo_ms = 10; /* Default to 10ms */ + ret->latency_hi_ms = 100; /* Default to 100ms */ + } + + return ret; +} + +static int +audiounit_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + AudioObjectID * hwdevs = NULL; + uint32_t i, hwdevcount = 0; + OSStatus err; + + if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) + return CUBEB_ERROR; + + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)); + (*collection)->count = 0; + + if (hwdevcount > 0) { + cubeb_device_info * cur; + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + for (i = 0; i < hwdevcount; i++) { + if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT)) != NULL) + (*collection)->device[(*collection)->count++] = cur; + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + for (i = 0; i < hwdevcount; i++) { + if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_INPUT)) != NULL) + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + free(hwdevs); + + return CUBEB_OK; +} + static struct cubeb_ops const audiounit_ops = { .init = audiounit_init, .get_backend_id = audiounit_get_backend_id, .get_max_channel_count = audiounit_get_max_channel_count, .get_min_latency = audiounit_get_min_latency, .get_preferred_sample_rate = audiounit_get_preferred_sample_rate, + .enumerate_devices = audiounit_enumerate_devices, .destroy = audiounit_destroy, .stream_init = audiounit_stream_init, .stream_destroy = audiounit_stream_destroy, diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index 7642e029ef6e..526aef0a4438 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -817,6 +817,7 @@ static struct cubeb_ops const opensl_ops = { .get_max_channel_count = opensl_get_max_channel_count, .get_min_latency = opensl_get_min_latency, .get_preferred_sample_rate = opensl_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = opensl_destroy, .stream_init = opensl_stream_init, .stream_destroy = opensl_stream_destroy, diff --git a/media/libcubeb/src/cubeb_pulse.c b/media/libcubeb/src/cubeb_pulse.c index c1ac2fb21c23..ef356617573a 100644 --- a/media/libcubeb/src/cubeb_pulse.c +++ b/media/libcubeb/src/cubeb_pulse.c @@ -26,6 +26,8 @@ X(pa_context_drain) \ X(pa_context_get_server_info) \ X(pa_context_get_sink_info_by_name) \ + X(pa_context_get_sink_info_list) \ + X(pa_context_get_source_info_list) \ X(pa_context_get_state) \ X(pa_context_new) \ X(pa_context_rttime_new) \ @@ -37,6 +39,7 @@ X(pa_frame_size) \ X(pa_operation_get_state) \ X(pa_operation_unref) \ + X(pa_proplist_gets) \ X(pa_rtclock_now) \ X(pa_stream_begin_write) \ X(pa_stream_cancel_write) \ @@ -205,7 +208,7 @@ stream_request_callback(pa_stream * s, size_t nbytes, void * u) if (stm->volume != PULSE_NO_GAIN) { uint32_t samples = size * stm->sample_spec.channels / frame_size ; - if (stm->sample_spec.format == PA_SAMPLE_S16LE || + if (stm->sample_spec.format == PA_SAMPLE_S16BE || stm->sample_spec.format == PA_SAMPLE_S16LE) { short * b = buffer; for (uint32_t i = 0; i < samples; i++) { @@ -724,12 +727,221 @@ pulse_stream_set_panning(cubeb_stream * stream, float panning) return CUBEB_OK; } +typedef struct { + char * default_sink_name; + char * default_source_name; + + cubeb_device_info ** devinfo; + uint32_t max; + uint32_t count; +} pulse_dev_list_data; + +static cubeb_device_fmt +pulse_format_to_cubeb_format(pa_sample_format_t format) +{ + switch (format) { + case PA_SAMPLE_S16LE: + return CUBEB_DEVICE_FMT_S16LE; + case PA_SAMPLE_S16BE: + return CUBEB_DEVICE_FMT_S16BE; + case PA_SAMPLE_FLOAT32LE: + return CUBEB_DEVICE_FMT_F32LE; + case PA_SAMPLE_FLOAT32BE: + return CUBEB_DEVICE_FMT_F32BE; + default: + return CUBEB_DEVICE_FMT_F32NE; + } +} + +static void +pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data) +{ + if (list_data->count == list_data->max) { + list_data->max += 8; + list_data->devinfo = realloc(list_data->devinfo, + sizeof(cubeb_device_info) * list_data->max); + } +} + +static cubeb_device_state +pulse_get_state_from_sink_port(pa_sink_port_info * info) +{ + if (info != NULL) { +#if PA_CHECK_VERSION(2, 0, 0) + if (info->available == PA_PORT_AVAILABLE_NO) + return CUBEB_DEVICE_STATE_UNPLUGGED; + else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ +#endif + return CUBEB_DEVICE_STATE_ENABLED; + } + + return CUBEB_DEVICE_STATE_DISABLED; +} + +static void +pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, + int eol, void * user_data) +{ + pulse_dev_list_data * list_data = user_data; + cubeb_device_info * devinfo; + const char * prop; + + (void)context; + + if (eol || info == NULL) + return; + + devinfo = calloc(1, sizeof(cubeb_device_info)); + + devinfo->device_id = strdup(info->name); + devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->friendly_name = strdup(info->description); + prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); + if (prop) + devinfo->group_id = strdup(prop); + prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); + if (prop) + devinfo->vendor_name = strdup(prop); + + devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT; + devinfo->state = pulse_get_state_from_sink_port(info->active_port); + devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0; + + devinfo->format = CUBEB_DEVICE_FMT_ALL; + devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->max_channels = info->channel_map.channels; + devinfo->min_rate = 1; + devinfo->max_rate = PA_RATE_MAX; + devinfo->default_rate = info->sample_spec.rate; + + devinfo->latency_lo_ms = 40; + devinfo->latency_hi_ms = 400; + + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; +} + +static cubeb_device_state +pulse_get_state_from_source_port(pa_source_port_info * info) +{ + if (info != NULL) { +#if PA_CHECK_VERSION(2, 0, 0) + if (info->available == PA_PORT_AVAILABLE_NO) + return CUBEB_DEVICE_STATE_UNPLUGGED; + else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ +#endif + return CUBEB_DEVICE_STATE_ENABLED; + } + + return CUBEB_DEVICE_STATE_DISABLED; +} + +static void +pulse_source_info_cb(pa_context * context, const pa_source_info * info, + int eol, void * user_data) +{ + pulse_dev_list_data * list_data = user_data; + cubeb_device_info * devinfo; + const char * prop; + + (void)context; + + if (eol) + return; + + devinfo = calloc(1, sizeof(cubeb_device_info)); + + devinfo->device_id = strdup(info->name); + devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->friendly_name = strdup(info->description); + prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); + if (prop) + devinfo->group_id = strdup(prop); + prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); + if (prop) + devinfo->vendor_name = strdup(prop); + + devinfo->type = CUBEB_DEVICE_TYPE_INPUT; + devinfo->state = pulse_get_state_from_source_port(info->active_port); + devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0; + + devinfo->format = CUBEB_DEVICE_FMT_ALL; + devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->max_channels = info->channel_map.channels; + devinfo->min_rate = 1; + devinfo->max_rate = PA_RATE_MAX; + devinfo->default_rate = info->sample_spec.rate; + + devinfo->latency_lo_ms = 1; + devinfo->latency_hi_ms = 10; + + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; +} + +static void +pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) +{ + pulse_dev_list_data * list_data = userdata; + + (void)c; + + free(list_data->default_sink_name); + free(list_data->default_source_name); + list_data->default_sink_name = strdup(i->default_sink_name); + list_data->default_source_name = strdup(i->default_source_name); +} + +static int +pulse_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0 }; + pa_operation * o; + uint32_t i; + + o = WRAP(pa_context_get_server_info)(context->context, + pulse_server_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + o = WRAP(pa_context_get_sink_info_list)(context->context, + pulse_sink_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + o = WRAP(pa_context_get_source_info_list)(context->context, + pulse_source_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + } + + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (user_data.count > 0 ? user_data.count - 1 : 0)); + (*collection)->count = user_data.count; + for (i = 0; i < user_data.count; i++) + (*collection)->device[i] = user_data.devinfo[i]; + + free(user_data.devinfo); + return CUBEB_OK; +} + static struct cubeb_ops const pulse_ops = { .init = pulse_init, .get_backend_id = pulse_get_backend_id, .get_max_channel_count = pulse_get_max_channel_count, .get_min_latency = pulse_get_min_latency, .get_preferred_sample_rate = pulse_get_preferred_sample_rate, + .enumerate_devices = pulse_enumerate_devices, .destroy = pulse_destroy, .stream_init = pulse_stream_init, .stream_destroy = pulse_stream_destroy, diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index e6d531a4f186..94f9961735fa 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -356,6 +356,7 @@ static struct cubeb_ops const sndio_ops = { .get_max_channel_count = sndio_get_max_channel_count, .get_min_latency = sndio_get_min_latency, .get_preferred_sample_rate = sndio_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = sndio_destroy, .stream_init = sndio_stream_init, .stream_destroy = sndio_stream_destroy, diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp index 2ba8509bb723..c7966f239c0b 100644 --- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -4,14 +4,15 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -// This enables assert in release, and lets us have debug-only code #if defined(HAVE_CONFIG_H) #include "config.h" #endif +#include #include #include #include #include +#include #include #include #include "cubeb/cubeb.h" @@ -22,11 +23,23 @@ #include #include -/**Taken from winbase.h, Not in MinGW.*/ +/* devicetopology.h missing in MinGW. */ +#ifndef __devicetopology_h__ +#include "cubeb_devicetopology.h" +#endif + +/* Taken from winbase.h, Not in MinGW. */ #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only #endif +#ifndef PKEY_Device_FriendlyName +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +#endif +#ifndef PKEY_Device_InstanceId +DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); // VT_LPWSTR +#endif + // #define LOGGING_ENABLED #ifdef LOGGING_ENABLED @@ -48,13 +61,13 @@ ms_to_hns(uint32_t ms) } uint32_t -hns_to_ms(uint32_t hns) +hns_to_ms(REFERENCE_TIME hns) { return hns / 10000; } double -hns_to_s(uint32_t hns) +hns_to_s(REFERENCE_TIME hns) { return static_cast(hns) / 10000000; } @@ -76,7 +89,7 @@ void SafeRelease(T * ptr) } /* This wraps a critical section to track the owner in debug mode, adapted from - * NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */ + NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */ class owned_critical_section { public: @@ -112,7 +125,7 @@ public: } /* This is guaranteed to have the good behaviour if it succeeds. The behaviour - * is undefined otherwise. */ + is undefined otherwise. */ void assert_current_thread_owns() { #ifdef DEBUG @@ -150,12 +163,12 @@ struct auto_com { if (result == RPC_E_CHANGED_MODE) { // This is not an error, COM was not initialized by this function, so it is // not necessary to uninit it. - LOG("COM already initialized in STA.\n"); + LOG("COM was already initialized in STA.\n"); } else if (result == S_FALSE) { // This is not an error. We are allowed to call CoInitializeEx more than // once, as long as it is matches by an CoUninitialize call. // We do that in the dtor which is guaranteed to be called. - LOG("COM already initialized in MTA\n"); + LOG("COM was already initialized in MTA\n"); } if (SUCCEEDED(result)) { CoUninitialize(); @@ -184,8 +197,8 @@ int setup_wasapi_stream(cubeb_stream * stm); struct cubeb { cubeb_ops const * ops; - /* Library dynamically opened to increase the render - * thread priority, and the two function pointers we need. */ + /* Library dynamically opened to increase the render thread priority, and + the two function pointers we need. */ HMODULE mmcss_module; set_mm_thread_characteristics_function set_mm_thread_characteristics; revert_mm_thread_characteristics_function revert_mm_thread_characteristics; @@ -196,9 +209,9 @@ class wasapi_endpoint_notification_client; struct cubeb_stream { cubeb * context; - /* Mixer pameters. We need to convert the input - * stream to this samplerate/channel layout, as WASAPI - * does not resample nor upmix itself. */ + /* Mixer pameters. We need to convert the input stream to this + samplerate/channel layout, as WASAPI * does not resample nor upmix + itself. */ cubeb_stream_params mix_params; cubeb_stream_params stream_params; /* The latency initially requested for this stream. */ @@ -208,10 +221,10 @@ struct cubeb_stream void * user_ptr; /* Lifetime considerations: - * - client, render_client, audio_clock and audio_stream_volume are interface - * pointer to the IAudioClient. - * - The lifetime for device_enumerator and notification_client, resampler, - * mix_buffer are the same as the cubeb_stream instance. */ + - client, render_client, audio_clock and audio_stream_volume are interface + pointer to the IAudioClient. + - The lifetime for device_enumerator and notification_client, resampler, + mix_buffer are the same as the cubeb_stream instance. */ /* Main handle on the WASAPI stream. */ IAudioClient * client; @@ -222,23 +235,23 @@ struct cubeb_stream /* Interface pointer to use the stream audio clock. */ IAudioClock * audio_clock; /* Frames written to the stream since it was opened. Reset on device - * change. Uses mix_params.rate. */ + change. Uses mix_params.rate. */ UINT64 frames_written; /* Frames written to the (logical) stream since it was first - * created. Updated on device change. Uses stream_params.rate. */ + created. Updated on device change. Uses stream_params.rate. */ UINT64 total_frames_written; /* Last valid reported stream position. Used to ensure the position - * reported by stream_get_position increases monotonically. */ + reported by stream_get_position increases monotonically. */ UINT64 prev_position; /* Device enumerator to be able to be notified when the default - * device change. */ + device change. */ IMMDeviceEnumerator * device_enumerator; /* Device notification client, to be able to be notified when the default - * audio device changes and route the audio to the new default audio output - * device */ + audio device changes and route the audio to the new default audio output + device */ wasapi_endpoint_notification_client * notification_client; /* This event is set by the stream_stop and stream_destroy - * function, so the render loop can exit properly. */ + function, so the render loop can exit properly. */ HANDLE shutdown_event; /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required. The reconfiguration is handled by the render loop thread. */ @@ -256,10 +269,10 @@ struct cubeb_stream /* Resampler instance. Resampling will only happen if necessary. */ cubeb_resampler * resampler; /* Buffer used to downmix or upmix to the number of channels the mixer has. - * its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */ + its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */ float * mix_buffer; /* Stream volume. Set via stream_set_volume and used to reset volume on - * device changes. */ + device changes. */ float volume; /* True if the stream is draining. */ bool draining; @@ -307,9 +320,14 @@ public: , reconfigure_event(event) { } + virtual ~wasapi_endpoint_notification_client() + { } + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) { + LOG("Audio device default changed.\n"); + /* we only support a single stream type for now. */ if (flow != eRender && role != eMultimedia) { return S_OK; @@ -317,14 +335,14 @@ public: BOOL ok = SetEvent(reconfigure_event); if (!ok) { - LOG("SetEvent on reconfigure_event failed: %x", GetLastError()); + LOG("SetEvent on reconfigure_event failed: %x\n", GetLastError()); } return S_OK; } /* The remaining methods are not implemented, they simply log when called (if - * log is enabled), for debugging. */ + log is enabled), for debugging. */ HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id) { LOG("Audio device added.\n"); @@ -422,8 +440,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel { XASSERT(in_channels >= out_channels); /* We could use a downmix matrix here, applying mixing weight based on the - * channel, but directsound and winmm simply drop the channels that cannot be - * rendered by the hardware, so we do the same for consistency. */ + channel, but directsound and winmm simply drop the channels that cannot be + rendered by the hardware, so we do the same for consistency. */ long out_index = 0; for (long i = 0; i < inframes * in_channels; i += in_channels) { for (int j = 0; j < out_channels; ++j) { @@ -433,8 +451,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel } } -/* This returns the size of a frame in the stream, - * before the eventual upmix occurs. */ +/* This returns the size of a frame in the stream, before the eventual upmix + occurs. */ static size_t frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames) { @@ -446,7 +464,7 @@ long refill(cubeb_stream * stm, float * data, long frames_needed) { /* If we need to upmix after resampling, resample into the mix buffer to - * avoid a copy. */ + avoid a copy. */ float * dest; if (should_upmix(stm) || should_downmix(stm)) { dest = stm->mix_buffer; @@ -465,12 +483,12 @@ refill(cubeb_stream * stm, float * data, long frames_needed) /* Go in draining mode if we got fewer frames than requested. */ if (out_frames < frames_needed) { - LOG("draining.\n"); + LOG("start draining.\n"); stm->draining = true; } /* If this is not true, there will be glitches. - * It is alright to have produced less frames if we are draining, though. */ + It is alright to have produced less frames if we are draining, though. */ XASSERT(out_frames == frames_needed || stm->draining); if (should_upmix(stm)) { @@ -493,16 +511,16 @@ wasapi_stream_render_loop(LPVOID stream) HANDLE wait_array[3] = {stm->shutdown_event, stm->reconfigure_event, stm->refill_event}; HANDLE mmcss_handle = NULL; HRESULT hr = 0; - bool first = true; DWORD mmcss_task_index = 0; auto_com com; if (!com.ok()) { LOG("COM initialization failed on render_loop thread.\n"); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); return 0; } /* We could consider using "Pro Audio" here for WebAudio and - * maybe WebRTC. */ + maybe WebRTC. */ mmcss_handle = stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index); if (!mmcss_handle) { @@ -529,7 +547,7 @@ wasapi_stream_render_loop(LPVOID stream) case WAIT_OBJECT_0: { /* shutdown */ is_playing = false; /* We don't check if the drain is actually finished here, we just want to - * shutdown. */ + shutdown. */ if (stm->draining) { stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); } @@ -542,13 +560,13 @@ wasapi_stream_render_loop(LPVOID stream) auto_lock lock(stm->stream_reset_lock); close_wasapi_stream(stm); /* Reopen a stream and start it immediately. This will automatically pick the - * new default device for this role. */ + new default device for this role. */ int r = setup_wasapi_stream(stm); if (r != CUBEB_OK) { /* Don't destroy the stream here, since we expect the caller to do so after the error has propagated via the state callback. */ is_playing = false; - hr = -1; + hr = E_FAIL; continue; } } @@ -560,14 +578,12 @@ wasapi_stream_render_loop(LPVOID stream) hr = stm->client->GetCurrentPadding(&padding); if (FAILED(hr)) { - LOG("Failed to get padding\n"); + LOG("Failed to get padding: %x\n", hr); is_playing = false; continue; } XASSERT(padding <= stm->buffer_frame_count); - long available = stm->buffer_frame_count - padding; - if (stm->draining) { if (padding == 0) { stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); @@ -576,6 +592,8 @@ wasapi_stream_render_loop(LPVOID stream) continue; } + long available = stm->buffer_frame_count - padding; + if (available == 0) { continue; } @@ -588,11 +606,11 @@ wasapi_stream_render_loop(LPVOID stream) hr = stm->render_client->ReleaseBuffer(wrote, 0); if (FAILED(hr)) { - LOG("failed to release buffer.\n"); + LOG("failed to release buffer: %x\n", hr); is_playing = false; } } else { - LOG("failed to get buffer.\n"); + LOG("failed to get buffer: %x\n", hr); is_playing = false; } } @@ -601,7 +619,7 @@ wasapi_stream_render_loop(LPVOID stream) XASSERT(stm->shutdown_event == wait_array[0]); if (++timeout_count >= timeout_limit) { is_playing = false; - hr = -1; + hr = E_FAIL; } break; default: @@ -636,7 +654,6 @@ HRESULT register_notification_client(cubeb_stream * stm) HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&stm->device_enumerator)); - if (FAILED(hr)) { LOG("Could not get device enumerator: %x\n", hr); return hr; @@ -645,7 +662,6 @@ HRESULT register_notification_client(cubeb_stream * stm) stm->notification_client = new wasapi_endpoint_notification_client(stm->reconfigure_event); hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client); - if (FAILED(hr)) { LOG("Could not register endpoint notification callback: %x\n", hr); return hr; @@ -677,16 +693,16 @@ HRESULT get_default_endpoint(IMMDevice ** device) NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)); if (FAILED(hr)) { - LOG("Could not get device enumerator.\n"); + LOG("Could not get device enumerator: %x\n", hr); return hr; } /* eMultimedia is okay for now ("Music, movies, narration, [...]"). - * We will need to change this when we distinguish streams by use-case, other - * possible values being eConsole ("Games, system notification sounds [...]") - * and eCommunication ("Voice communication"). */ + We will need to change this when we distinguish streams by use-case, other + possible values being eConsole ("Games, system notification sounds [...]") + and eCommunication ("Voice communication"). */ hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, device); if (FAILED(hr)) { - LOG("Could not get default audio endpoint. %d\n", __LINE__); + LOG("Could not get default audio endpoint: %x\n", hr); SafeRelease(enumerator); return hr; } @@ -781,12 +797,15 @@ int wasapi_init(cubeb ** context, char const * context_name) IMMDevice * device; hr = get_default_endpoint(&device); if (FAILED(hr)) { - LOG("Could not get device.\n"); + LOG("Could not get device: %x\n", hr); return CUBEB_ERROR; } SafeRelease(device); cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb)); + if (!ctx) { + return CUBEB_ERROR; + } ctx->ops = &wasapi_ops; @@ -906,10 +925,14 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten return CUBEB_ERROR; } + if (params.format != CUBEB_SAMPLE_FLOAT32NE) { + return CUBEB_ERROR_INVALID_FORMAT; + } + IMMDevice * device; hr = get_default_endpoint(&device); if (FAILED(hr)) { - LOG("Could not get default endpoint:%x.\n", hr); + LOG("Could not get default endpoint: %x\n", hr); return CUBEB_ERROR; } @@ -918,7 +941,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten NULL, (void **)&client); SafeRelease(device); if (FAILED(hr)) { - LOG("Could not activate device for latency: %x.\n", hr); + LOG("Could not activate device for latency: %x\n", hr); return CUBEB_ERROR; } @@ -926,15 +949,15 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten hr = client->GetDevicePeriod(&default_period, NULL); if (FAILED(hr)) { SafeRelease(client); - LOG("Could not get device period: %x.\n", hr); + LOG("Could not get device period: %x\n", hr); return CUBEB_ERROR; } LOG("default device period: %ld\n", default_period); /* According to the docs, the best latency we can achieve is by synchronizing - * the stream and the engine. - * http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */ + the stream and the engine. + http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */ *latency_ms = hns_to_ms(default_period); SafeRelease(client); @@ -983,22 +1006,22 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) void wasapi_stream_destroy(cubeb_stream * stm); -/* Based on the mix format and the stream format, try to find a way to play what - * the user requested. */ +/* Based on the mix format and the stream format, try to find a way to play + what the user requested. */ static void handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params) { /* Common case: the hardware is stereo. Up-mixing and down-mixing will be - * handled in the callback. */ + handled in the callback. */ if ((*mix_format)->nChannels <= 2) { return; } /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1], - * so the reinterpret_cast below should be safe. In practice, this is not - * true, and we just want to bail out and let the rest of the code find a good - * conversion path instead of trying to make WASAPI do it by itself. - * [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ + so the reinterpret_cast below should be safe. In practice, this is not + true, and we just want to bail out and let the rest of the code find a good + conversion path instead of trying to make WASAPI do it by itself. + [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ if ((*mix_format)->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { return; } @@ -1009,7 +1032,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm; /* The hardware is in surround mode, we want to only use front left and front - * right. Try that, and check if it works. */ + right. Try that, and check if it works. */ switch (stream_params->channels) { case 1: /* Mono */ format_pcm->dwChannelMask = KSAUDIO_SPEAKER_MONO; @@ -1036,7 +1059,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub if (hr == S_FALSE) { /* Not supported, but WASAPI gives us a suggestion. Use it, and handle the - * eventual upmix/downmix ourselves */ + eventual upmix/downmix ourselves */ LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels); WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast(closest); XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat); @@ -1044,11 +1067,13 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub *mix_format = closest; } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) { /* Not supported, no suggestion. This should not happen, but it does in the - * field with some sound cards. We restore the mix format, and let the rest - * of the code figure out the right conversion path. */ + field with some sound cards. We restore the mix format, and let the rest + of the code figure out the right conversion path. */ *reinterpret_cast(*mix_format) = hw_mix_format; } else if (hr == S_OK) { LOG("Requested format accepted by WASAPI.\n"); + } else { + LOG("IsFormatSupported unhandled error: %x\n", hr); } } @@ -1074,7 +1099,7 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* Get a client. We will get all other interfaces we need from - * this pointer. */ + this pointer. */ hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void **)&stm->client); @@ -1085,7 +1110,7 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* We have to distinguish between the format the mixer uses, - * and the format the stream we want to play uses. */ + and the format the stream we want to play uses. */ hr = stm->client->GetMixFormat(&mix_format); if (FAILED(hr)) { LOG("Could not fetch current mix format from the audio client: error: %x\n", hr); @@ -1095,7 +1120,7 @@ int setup_wasapi_stream(cubeb_stream * stm) handle_channel_layout(stm, &mix_format, &stm->stream_params); /* Shared mode WASAPI always supports float32 sample format, so this - * is safe. */ + is safe. */ stm->mix_params.format = CUBEB_SAMPLE_FLOAT32NE; stm->mix_params.rate = mix_format->nSamplesPerSec; stm->mix_params.channels = mix_format->nChannels; @@ -1107,17 +1132,15 @@ int setup_wasapi_stream(cubeb_stream * stm) 0, mix_format, NULL); - CoTaskMemFree(mix_format); - if (FAILED(hr)) { - LOG("Unable to initialize audio client: %x.\n", hr); + LOG("Unable to initialize audio client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetBufferSize(&stm->buffer_frame_count); if (FAILED(hr)) { - LOG("Could not get the buffer size from the client %x.\n", hr); + LOG("Could not get the buffer size from the client: %x\n", hr); return CUBEB_ERROR; } @@ -1127,21 +1150,21 @@ int setup_wasapi_stream(cubeb_stream * stm) hr = stm->client->SetEventHandle(stm->refill_event); if (FAILED(hr)) { - LOG("Could set the event handle for the client %x.\n", hr); + LOG("Could set the event handle for the client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetService(__uuidof(IAudioRenderClient), (void **)&stm->render_client); if (FAILED(hr)) { - LOG("Could not get the render client %x.\n", hr); + LOG("Could not get the render client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetService(__uuidof(IAudioStreamVolume), (void **)&stm->audio_stream_volume); if (FAILED(hr)) { - LOG("Could not get the IAudioStreamVolume %x.\n", hr); + LOG("Could not get the IAudioStreamVolume: %x\n", hr); return CUBEB_ERROR; } @@ -1149,7 +1172,7 @@ int setup_wasapi_stream(cubeb_stream * stm) hr = stm->client->GetService(__uuidof(IAudioClock), (void **)&stm->audio_clock); if (FAILED(hr)) { - LOG("Could not get the IAudioClock %x.\n", hr); + LOG("Could not get the IAudioClock: %x\n", hr); return CUBEB_ERROR; } @@ -1159,9 +1182,9 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* If we are playing a mono stream, we only resample one channel, - * and copy it over, so we are always resampling the number - * of channels of the stream, not the number of channels - * that WASAPI wants. */ + and copy it over, so we are always resampling the number + of channels of the stream, not the number of channels + that WASAPI wants. */ stm->resampler = cubeb_resampler_create(stm, stm->stream_params, stm->mix_params.rate, stm->data_callback, @@ -1191,6 +1214,10 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, XASSERT(context && stream); + if (stream_params.format != CUBEB_SAMPLE_FLOAT32NE) { + return CUBEB_ERROR_INVALID_FORMAT; + } + cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream)); XASSERT(stm); @@ -1235,7 +1262,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, hr = register_notification_client(stm); if (FAILED(hr)) { /* this is not fatal, we can still play audio, but we won't be able - * to keep using the default audio endpoint if it changes. */ + to keep using the default audio endpoint if it changes. */ LOG("failed to register notification client, %x\n", hr); } @@ -1325,7 +1352,7 @@ int wasapi_stream_start(cubeb_stream * stm) LOG("could not start the stream after reconfig: %x\n", hr); return CUBEB_ERROR; } - } else if (FAILED(hr)) { + } else if (FAILED(hr)) { LOG("could not start the stream.\n"); return CUBEB_ERROR; } @@ -1402,13 +1429,16 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) auto_lock lock(stm->stream_reset_lock); /* The GetStreamLatency method only works if the - * AudioClient has been initialized. */ + AudioClient has been initialized. */ if (!stm->client) { return CUBEB_ERROR; } REFERENCE_TIME latency_hns; - stm->client->GetStreamLatency(&latency_hns); + HRESULT hr = stm->client->GetStreamLatency(&latency_hns); + if (FAILED(hr)) { + return CUBEB_ERROR; + } double latency_s = hns_to_s(latency_hns); *latency = static_cast(latency_s * stm->stream_params.rate); @@ -1428,12 +1458,245 @@ int wasapi_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } +static char * +wstr_to_utf8(LPCWSTR str) +{ + char * ret = NULL; + int size; + + size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, 0, NULL, NULL); + if (size > 0) { + ret = (char *) malloc(size); + ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL); + } + + return ret; +} + +static IMMDevice * +wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev) +{ + IMMDevice * ret = NULL; + IDeviceTopology * devtopo = NULL; + IConnector * connector = NULL; + + if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&devtopo)) && + SUCCEEDED(devtopo->GetConnector(0, &connector))) { + LPWSTR filterid; + if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&filterid))) { + if (FAILED(enumerator->GetDevice(filterid, &ret))) + ret = NULL; + CoTaskMemFree(filterid); + } + } + + SafeRelease(connector); + SafeRelease(devtopo); + return ret; +} + +static BOOL +wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id, + IMMDeviceEnumerator * enumerator) +{ + BOOL ret = FALSE; + IMMDevice * dev; + HRESULT hr; + + hr = enumerator->GetDefaultAudioEndpoint(flow, role, &dev); + if (SUCCEEDED(hr)) { + LPWSTR defdevid = NULL; + if (SUCCEEDED(dev->GetId(&defdevid))) + ret = (wcscmp(defdevid, device_id) == 0); + if (defdevid != NULL) + CoTaskMemFree(defdevid); + SafeRelease(dev); + } + + return ret; +} + +static cubeb_device_info * +wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev) +{ + IMMEndpoint * endpoint = NULL; + IMMDevice * devnode; + IAudioClient * client = NULL; + cubeb_device_info * ret = NULL; + EDataFlow flow; + LPWSTR device_id = NULL; + DWORD state = DEVICE_STATE_NOTPRESENT; + IPropertyStore * propstore = NULL; + PROPVARIANT propvar; + REFERENCE_TIME def_period, min_period; + HRESULT hr; + + PropVariantInit(&propvar); + + hr = dev->QueryInterface(IID_PPV_ARGS(&endpoint)); + if (FAILED(hr)) goto done; + + hr = endpoint->GetDataFlow(&flow); + if (FAILED(hr)) goto done; + + hr = dev->GetId(&device_id); + if (FAILED(hr)) goto done; + + hr = dev->OpenPropertyStore(STGM_READ, &propstore); + if (FAILED(hr)) goto done; + + hr = dev->GetState(&state); + if (FAILED(hr)) goto done; + + ret = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info)); + + ret->devid = ret->device_id = wstr_to_utf8(device_id); + hr = propstore->GetValue(PKEY_Device_FriendlyName, &propvar); + if (SUCCEEDED(hr)) + ret->friendly_name = wstr_to_utf8(propvar.pwszVal); + + devnode = wasapi_get_device_node(enumerator, dev); + if (devnode != NULL) { + IPropertyStore * ps = NULL; + hr = devnode->OpenPropertyStore(STGM_READ, &ps); + if (FAILED(hr)) goto done; + + PropVariantClear(&propvar); + hr = ps->GetValue(PKEY_Device_InstanceId, &propvar); + if (SUCCEEDED(hr)) { + ret->group_id = wstr_to_utf8(propvar.pwszVal); + } + SafeRelease(ps); + } + + ret->preferred = CUBEB_DEVICE_PREF_NONE; + if (wasapi_is_default_device(flow, eMultimedia, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA); + if (wasapi_is_default_device(flow, eCommunications, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE); + if (wasapi_is_default_device(flow, eConsole, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_NOTIFICATION); + + if (flow == eRender) ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT; + switch (state) { + case DEVICE_STATE_ACTIVE: + ret->state = CUBEB_DEVICE_STATE_ENABLED; + break; + case DEVICE_STATE_UNPLUGGED: + ret->state = CUBEB_DEVICE_STATE_UNPLUGGED; + break; + default: + ret->state = CUBEB_DEVICE_STATE_DISABLED; + break; + }; + + ret->format = CUBEB_DEVICE_FMT_F32NE; /* cubeb only supports 32bit float at the moment */ + ret->default_format = CUBEB_DEVICE_FMT_F32NE; + PropVariantClear(&propvar); + hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &propvar); + if (SUCCEEDED(hr) && propvar.vt == VT_BLOB) { + if (propvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) { + const PCMWAVEFORMAT * pcm = reinterpret_cast(propvar.blob.pBlobData); + + ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec; + ret->max_channels = pcm->wf.nChannels; + } else if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX)) { + WAVEFORMATEX* wfx = reinterpret_cast(propvar.blob.pBlobData); + + if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || + wfx->wFormatTag == WAVE_FORMAT_PCM) { + ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec; + ret->max_channels = wfx->nChannels; + } + } + } + + if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) && + SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { + ret->latency_lo_ms = hns_to_ms(min_period); + ret->latency_hi_ms = hns_to_ms(def_period); + } else { + ret->latency_lo_ms = 0; + ret->latency_hi_ms = 0; + } + SafeRelease(client); + +done: + SafeRelease(devnode); + SafeRelease(endpoint); + SafeRelease(propstore); + if (device_id != NULL) + CoTaskMemFree(device_id); + PropVariantClear(&propvar); + return ret; +} + +static int +wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** out) +{ + auto_com com; + IMMDeviceEnumerator * enumerator; + IMMDeviceCollection * collection; + IMMDevice * dev; + cubeb_device_info * cur; + HRESULT hr; + UINT cc, i; + EDataFlow flow; + + *out = NULL; + + if (!com.ok()) + return CUBEB_ERROR; + + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)); + if (FAILED(hr)) { + LOG("Could not get device enumerator: %x\n", hr); + return CUBEB_ERROR; + } + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender; + else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture; + else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll; + else return CUBEB_ERROR; + + hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection); + if (FAILED(hr)) { + LOG("Could not enumerate audio endpoints: %x\n", hr); + return CUBEB_ERROR; + } + + hr = collection->GetCount(&cc); + if (FAILED(hr)) { + LOG("IMMDeviceCollection::GetCount() failed: %x\n", hr); + return CUBEB_ERROR; + } + *out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0)); + (*out)->count = 0; + for (i = 0; i < cc; i++) { + hr = collection->Item(i, &dev); + if (FAILED(hr)) { + LOG("IMMDeviceCollection::Item(%u) failed: %x\n", i-1, hr); + } else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) { + (*out)->device[(*out)->count++] = cur; + } + } + + SafeRelease(collection); + SafeRelease(enumerator); + return CUBEB_OK; +} + cubeb_ops const wasapi_ops = { /*.init =*/ wasapi_init, /*.get_backend_id =*/ wasapi_get_backend_id, /*.get_max_channel_count =*/ wasapi_get_max_channel_count, /*.get_min_latency =*/ wasapi_get_min_latency, /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate, + /*.enumerate_devices =*/ wasapi_enumerate_devices, /*.destroy =*/ wasapi_destroy, /*.stream_init =*/ wasapi_stream_init, /*.stream_destroy =*/ wasapi_stream_destroy, diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index 029c867c4a03..da5828f1d45b 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -8,7 +8,6 @@ #undef WINVER #define WINVER 0x0501 #undef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN #include #include @@ -27,15 +26,46 @@ #endif /**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/ +#ifndef WAVE_FORMAT_48M08 +#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ +#endif +#ifndef WAVE_FORMAT_48M16 +#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */ +#endif +#ifndef WAVE_FORMAT_48S08 +#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */ +#endif #ifndef WAVE_FORMAT_48S16 #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ #endif +#ifndef WAVE_FORMAT_96M08 +#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ +#endif +#ifndef WAVE_FORMAT_96M16 +#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */ +#endif +#ifndef WAVE_FORMAT_96S08 +#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */ +#endif +#ifndef WAVE_FORMAT_96S16 +#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ +#endif /**Taken from winbase.h, also not in MinGW.*/ #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only #endif +#ifndef DRVM_MAPPER +#define DRVM_MAPPER (0x2000) +#endif +#ifndef DRVM_MAPPER_PREFERRED_GET +#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) +#endif +#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET +#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) +#endif + #define CUBEB_STREAM_MAX 32 #define NBUFS 4 @@ -671,12 +701,307 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } +#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16) +#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16) +#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16) +#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16) +#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16) +static void +winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) +{ + if (formats & MM_11025HZ_MASK) { + info->min_rate = 11025; + info->default_rate = 11025; + info->max_rate = 11025; + } + if (formats & MM_22050HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 22050; + info->max_rate = 22050; + info->default_rate = 22050; + } + if (formats & MM_44100HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 44100; + info->max_rate = 44100; + info->default_rate = 44100; + } + if (formats & MM_48000HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 48000; + info->max_rate = 48000; + info->default_rate = 48000; + } + if (formats & MM_96000HZ_MASK) { + if (info->min_rate == 0) { + info->min_rate = 96000; + info->default_rate = 96000; + } + info->max_rate = 96000; + } +} + + +#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \ + WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16) +static int +winmm_query_supported_formats(UINT devid, DWORD formats, + cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt) +{ + WAVEFORMATEXTENSIBLE wfx; + + if (formats & MM_S16_MASK) + *deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE; + else + *deffmt = *supfmt = 0; + + ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE)); + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.Format.nChannels = 2; + wfx.Format.nSamplesPerSec = 44100; + wfx.Format.wBitsPerSample = 32; + wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.cbSize = 22; + wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; + wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR) + *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE); + + return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR; +} + +static char * +guid_to_cstr(LPGUID guid) +{ + char * ret = malloc(sizeof(char) * 40); + _snprintf(ret, sizeof(char) * 40, + "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return ret; +} + +static cubeb_device_pref +winmm_query_preferred_out_device(UINT devid) +{ + DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; + cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; + + if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == mmpref) + ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; + + if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == compref) + ret |= CUBEB_DEVICE_PREF_VOICE; + + return ret; +} + +static char * +device_id_idx(UINT devid) +{ + char * ret = (char *)malloc(sizeof(char)*16); + _snprintf(ret, 16, "%u", devid); + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = guid_to_cstr(&caps->ProductGuid); + ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); + + ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_out_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = NULL; + ret->vendor_name = NULL; + + ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_out_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_pref +winmm_query_preferred_in_device(UINT devid) +{ + DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; + cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; + + if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == mmpref) + ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; + + if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == compref) + ret |= CUBEB_DEVICE_PREF_VOICE; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = guid_to_cstr(&caps->ProductGuid); + ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); + + ret->type = CUBEB_DEVICE_TYPE_INPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_in_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = NULL; + ret->vendor_name = NULL; + + ret->type = CUBEB_DEVICE_TYPE_INPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_in_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static int +winmm_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + UINT i, incount, outcount, total; + cubeb_device_info * cur; + + outcount = waveOutGetNumDevs(); + incount = waveInGetNumDevs(); + total = outcount + incount; + if (total > 0) { + total -= 1; + } + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * total); + (*collection)->count = 0; + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + WAVEOUTCAPSA woc; + WAVEOUTCAPS2A woc2; + + ZeroMemory(&woc, sizeof(woc)); + ZeroMemory(&woc2, sizeof(woc2)); + + for (i = 0; i < outcount; i++) { + if ((waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_outcaps2(&woc2, i)) != NULL) || + (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_outcaps(&woc, i)) != NULL) + ) { + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + WAVEINCAPSA wic; + WAVEINCAPS2A wic2; + + ZeroMemory(&wic, sizeof(wic)); + ZeroMemory(&wic2, sizeof(wic2)); + + for (i = 0; i < incount; i++) { + if ((waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_incaps2(&wic2, i)) != NULL) || + (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_incaps(&wic, i)) != NULL) + ) { + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + return CUBEB_OK; +} + static struct cubeb_ops const winmm_ops = { /*.init =*/ winmm_init, /*.get_backend_id =*/ winmm_get_backend_id, /*.get_max_channel_count=*/ winmm_get_max_channel_count, /*.get_min_latency=*/ winmm_get_min_latency, /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate, + /*.enumerate_devices =*/ winmm_enumerate_devices, /*.destroy =*/ winmm_destroy, /*.stream_init =*/ winmm_stream_init, /*.stream_destroy =*/ winmm_stream_destroy, diff --git a/media/libcubeb/tests/common.h b/media/libcubeb/tests/common.h index 69445b86c6f5..47cc28cc7f45 100644 --- a/media/libcubeb/tests/common.h +++ b/media/libcubeb/tests/common.h @@ -27,3 +27,4 @@ void delay(unsigned int ms) #if !defined(M_PI) #define M_PI 3.14159265358979323846 #endif + diff --git a/media/libcubeb/tests/moz.build b/media/libcubeb/tests/moz.build index 4088f7c1259c..e98d8d98d21e 100644 --- a/media/libcubeb/tests/moz.build +++ b/media/libcubeb/tests/moz.build @@ -4,6 +4,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +DEFINES['CUBEB_GECKO_BUILD'] = True + GeckoCppUnitTests([ 'test_tone' ]) diff --git a/media/libcubeb/tests/test_audio.cpp b/media/libcubeb/tests/test_audio.cpp index 69c166638b47..6a6ebf53caaa 100644 --- a/media/libcubeb/tests/test_audio.cpp +++ b/media/libcubeb/tests/test_audio.cpp @@ -10,7 +10,7 @@ #ifdef NDEBUG #undef NDEBUG #endif -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 600 #include #include #include @@ -19,7 +19,9 @@ #include "cubeb/cubeb.h" #include "common.h" +#ifdef CUBEB_GECKO_BUILD #include "TestHarness.h" +#endif #define MAX_NUM_CHANNELS 32 @@ -45,6 +47,8 @@ typedef struct { synth_state* synth_create(int num_channels, float sample_rate) { synth_state* synth = (synth_state *) malloc(sizeof(synth_state)); + if (!synth) + return NULL; for(int i=0;i < MAX_NUM_CHANNELS;++i) synth->phase[i] = 0.0f; synth->num_channels = num_channels; @@ -106,6 +110,12 @@ int supports_float32(const char* backend_id) strcmp(backend_id, "audiotrack") != 0); } +/* The WASAPI backend only supports float. */ +int supports_int16(const char* backend_id) +{ + return strcmp(backend_id, "wasapi") != 0; +} + /* Some backends don't have code to deal with more than mono or stereo. */ int supports_channel_count(const char* backend_id, int nchannels) { @@ -131,6 +141,7 @@ int run_test(int num_channels, int sampling_rate, int is_float) backend_id = cubeb_get_backend_id(ctx); if ((is_float && !supports_float32(backend_id)) || + (!is_float && !supports_int16(backend_id)) || !supports_channel_count(backend_id, num_channels)) { /* don't treat this as a test failure. */ goto cleanup; @@ -168,22 +179,30 @@ cleanup: return r; } -int run_panning_volume_test() +int run_panning_volume_test(int is_float) { int r = CUBEB_OK; cubeb *ctx = NULL; synth_state* synth = NULL; cubeb_stream *stream = NULL; + const char * backend_id = NULL; r = cubeb_init(&ctx, "Cubeb audio test"); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb library\n"); goto cleanup; } + backend_id = cubeb_get_backend_id(ctx); + + if ((is_float && !supports_float32(backend_id)) || + (!is_float && !supports_int16(backend_id))) { + /* don't treat this as a test failure. */ + goto cleanup; + } cubeb_stream_params params; - params.format = CUBEB_SAMPLE_S16NE; + params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE; params.rate = 44100; params.channels = 2; @@ -194,7 +213,7 @@ int run_panning_volume_test() } r = cubeb_stream_init(ctx, &stream, "test tone", params, - 100, data_cb_short, state_cb, synth); + 100, is_float ? data_cb_float : data_cb_short, state_cb, synth); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb stream: %d\n", r); goto cleanup; @@ -262,9 +281,12 @@ void run_channel_rate_test() int main(int argc, char *argv[]) { +#ifdef CUBEB_GECKO_BUILD ScopedXPCOM xpcom("test_audio"); +#endif - assert(run_panning_volume_test() == CUBEB_OK); + assert(run_panning_volume_test(0) == CUBEB_OK); + assert(run_panning_volume_test(1) == CUBEB_OK); run_channel_rate_test(); return CUBEB_OK; diff --git a/media/libcubeb/tests/test_latency.cpp b/media/libcubeb/tests/test_latency.cpp index c1b2ae2da109..5b4da8e7da2a 100644 --- a/media/libcubeb/tests/test_latency.cpp +++ b/media/libcubeb/tests/test_latency.cpp @@ -5,12 +5,17 @@ #include #include #include +#ifdef CUBEB_GECKO_BUILD #include "TestHarness.h" +#endif + #define LOG(msg) fprintf(stderr, "%s\n", msg); int main(int argc, char * argv[]) { +#ifdef CUBEB_GECKO_BUILD ScopedXPCOM xpcom("test_latency"); +#endif cubeb * ctx = NULL; int r; diff --git a/media/libcubeb/tests/test_sanity.cpp b/media/libcubeb/tests/test_sanity.cpp index 1d7e5562bfaf..ca9ca1bf1b6f 100644 --- a/media/libcubeb/tests/test_sanity.cpp +++ b/media/libcubeb/tests/test_sanity.cpp @@ -7,27 +7,33 @@ #ifdef NDEBUG #undef NDEBUG #endif -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 600 #include "cubeb/cubeb.h" #include #include #include #include #include "common.h" +#ifdef CUBEB_GECKO_BUILD #include "TestHarness.h" +#endif #if (defined(_WIN32) || defined(__WIN32__)) #define __func__ __FUNCTION__ #endif #define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0])) -#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__); -#define END_TEST fprintf(stderr, "END %s\n", __func__); +#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__) +#define END_TEST fprintf(stderr, "END %s\n", __func__) #define STREAM_LATENCY 100 #define STREAM_RATE 44100 #define STREAM_CHANNELS 1 +#if (defined(_WIN32) || defined(__WIN32__)) +#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE +#else #define STREAM_FORMAT CUBEB_SAMPLE_S16LE +#endif static int dummy; static uint64_t total_frames_written; @@ -37,7 +43,12 @@ static long test_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes) { assert(stm && user_ptr == &dummy && p && nframes > 0); +#if (defined(_WIN32) || defined(__WIN32__)) + memset(p, 0, nframes * sizeof(float)); +#else memset(p, 0, nframes * sizeof(short)); +#endif + total_frames_written += nframes; if (delay_callback) { delay(10); @@ -57,9 +68,9 @@ test_init_destroy_context(void) cubeb * ctx; char const* backend_id; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); @@ -70,8 +81,8 @@ test_init_destroy_context(void) cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_init_destroy_multiple_contexts(void) @@ -82,20 +93,20 @@ test_init_destroy_multiple_contexts(void) int order[4] = {2, 0, 3, 1}; assert(ARRAY_LENGTH(ctx) == ARRAY_LENGTH(order)); - BEGIN_TEST + BEGIN_TEST; - for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { - r = cubeb_init(&ctx[i], NULL); - assert(r == 0 && ctx[i]); - } + for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { + r = cubeb_init(&ctx[i], NULL); + assert(r == 0 && ctx[i]); + } /* destroy in a different order */ for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { cubeb_destroy(ctx[order[i]]); } - END_TEST - } + END_TEST; +} static void test_context_variables(void) @@ -105,14 +116,14 @@ test_context_variables(void) uint32_t value; cubeb_stream_params params; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_context_variables"); + r = cubeb_init(&ctx, "test_context_variables"); assert(r == 0 && ctx); - params.channels = 2; - params.format = CUBEB_SAMPLE_S16LE; - params.rate = 44100; + params.channels = STREAM_CHANNELS; + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; r = cubeb_get_min_latency(ctx, params, &value); assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED); if (r == CUBEB_OK) { @@ -127,8 +138,8 @@ test_context_variables(void) cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_init_destroy_stream(void) @@ -138,9 +149,9 @@ test_init_destroy_stream(void) cubeb_stream * stream; cubeb_stream_params params; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); params.format = STREAM_FORMAT; @@ -154,8 +165,8 @@ test_init_destroy_stream(void) cubeb_stream_destroy(stream); cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_init_destroy_multiple_streams(void) @@ -166,9 +177,9 @@ test_init_destroy_multiple_streams(void) cubeb_stream * stream[8]; cubeb_stream_params params; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); params.format = STREAM_FORMAT; @@ -188,8 +199,8 @@ test_init_destroy_multiple_streams(void) cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_configure_stream(void) @@ -199,9 +210,9 @@ test_configure_stream(void) cubeb_stream * stream; cubeb_stream_params params; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); params.format = STREAM_FORMAT; @@ -220,8 +231,8 @@ test_configure_stream(void) cubeb_stream_destroy(stream); cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_init_start_stop_destroy_multiple_streams(int early, int delay_ms) @@ -232,9 +243,9 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms) cubeb_stream * stream[8]; cubeb_stream_params params; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); params.format = STREAM_FORMAT; @@ -281,8 +292,8 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms) cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_init_destroy_multiple_contexts_and_streams(void) @@ -295,9 +306,9 @@ test_init_destroy_multiple_contexts_and_streams(void) size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx); assert(ARRAY_LENGTH(ctx) * streams_per_ctx == ARRAY_LENGTH(stream)); - BEGIN_TEST + BEGIN_TEST; - params.format = STREAM_FORMAT; + params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; @@ -320,8 +331,8 @@ test_init_destroy_multiple_contexts_and_streams(void) cubeb_destroy(ctx[i]); } - END_TEST - } + END_TEST; +} static void test_basic_stream_operations(void) @@ -332,9 +343,9 @@ test_basic_stream_operations(void) cubeb_stream_params params; uint64_t position; - BEGIN_TEST + BEGIN_TEST; - r = cubeb_init(&ctx, "test_sanity"); + r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); params.format = STREAM_FORMAT; @@ -366,8 +377,8 @@ test_basic_stream_operations(void) cubeb_stream_destroy(stream); cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static void test_stream_position(void) @@ -379,9 +390,9 @@ test_stream_position(void) cubeb_stream_params params; uint64_t position, last_position; - BEGIN_TEST + BEGIN_TEST; - total_frames_written = 0; + total_frames_written = 0; r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); @@ -456,8 +467,8 @@ test_stream_position(void) cubeb_stream_destroy(stream); cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} static int do_drain; static int got_drain; @@ -495,9 +506,9 @@ test_drain(void) cubeb_stream_params params; uint64_t position; - BEGIN_TEST + BEGIN_TEST; - total_frames_written = 0; + total_frames_written = 0; r = cubeb_init(&ctx, "test_sanity"); assert(r == 0 && ctx); @@ -539,8 +550,8 @@ test_drain(void) cubeb_stream_destroy(stream); cubeb_destroy(ctx); - END_TEST - } + END_TEST; +} int is_windows_7() { @@ -572,7 +583,9 @@ int is_windows_7() int main(int argc, char * argv[]) { +#ifdef CUBEB_GECKO_BUILD ScopedXPCOM xpcom("test_sanity"); +#endif test_init_destroy_context(); test_init_destroy_multiple_contexts(); diff --git a/media/libcubeb/tests/test_tone.cpp b/media/libcubeb/tests/test_tone.cpp index ae50c69281b5..d0cee22b372e 100644 --- a/media/libcubeb/tests/test_tone.cpp +++ b/media/libcubeb/tests/test_tone.cpp @@ -9,17 +9,25 @@ #ifdef NDEBUG #undef NDEBUG #endif -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 600 #include #include #include #include +#include #include "cubeb/cubeb.h" #include "common.h" +#ifdef CUBEB_GECKO_BUILD #include "TestHarness.h" +#endif #define SAMPLE_FREQUENCY 48000 +#if (defined(_WIN32) || defined(__WIN32__)) +#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE +#else +#define STREAM_FORMAT CUBEB_SAMPLE_S16LE +#endif /* store the phase of the generated waveform */ struct cb_user_data { @@ -29,7 +37,12 @@ struct cb_user_data { long data_cb(cubeb_stream *stream, void *user, void *buffer, long nframes) { struct cb_user_data *u = (struct cb_user_data *)user; +#if (defined(_WIN32) || defined(__WIN32__)) + float *b = (float *)buffer; +#else short *b = (short *)buffer; +#endif + float t1, t2; int i; if (stream == NULL || u == NULL) @@ -38,10 +51,24 @@ long data_cb(cubeb_stream *stream, void *user, void *buffer, long nframes) /* generate our test tone on the fly */ for (i = 0; i < nframes; i++) { /* North American dial tone */ - b[i] = 16000*sin(2*M_PI*(i + u->position)*350/SAMPLE_FREQUENCY); - b[i] += 16000*sin(2*M_PI*(i + u->position)*440/SAMPLE_FREQUENCY); + t1 = sin(2*M_PI*(i + u->position)*350/SAMPLE_FREQUENCY); + t2 = sin(2*M_PI*(i + u->position)*440/SAMPLE_FREQUENCY); +#if (defined(_WIN32) || defined(__WIN32__)) + b[i] = 0.5 * t1; + b[i] += 0.5 * t2; +#else + b[i] = (SHRT_MAX / 2) * t1; + b[i] += (SHRT_MAX / 2) * t2; +#endif /* European dial tone */ - /*b[i] = 30000*sin(2*M_PI*(i + u->position)*425/SAMPLE_FREQUENCY);*/ + /* + t1 = sin(2*M_PI*(i + u->position)*425/SAMPLE_FREQUENCY); +#if (defined(_WIN32) || defined(__WIN32__)) + b[i] = t1; +#else + b[i] = SHRT_MAX * t1; +#endif + */ } /* remember our phase to avoid clicking on buffer transitions */ /* we'll still click if position overflows */ @@ -73,7 +100,9 @@ void state_cb(cubeb_stream *stream, void *user, cubeb_state state) int main(int argc, char *argv[]) { +#ifdef CUBEB_GECKO_BUILD ScopedXPCOM xpcom("test_tone"); +#endif cubeb *ctx; cubeb_stream *stream; @@ -87,7 +116,7 @@ int main(int argc, char *argv[]) return r; } - params.format = CUBEB_SAMPLE_S16NE; + params.format = STREAM_FORMAT; params.rate = SAMPLE_FREQUENCY; params.channels = 1; From 76976eadbbe1b70c9ec26821a65089fc552b4c6b Mon Sep 17 00:00:00 2001 From: Ralph Giles Date: Tue, 1 Dec 2015 13:34:57 -0800 Subject: [PATCH 053/102] Bug 1228721 - Conditionalize webvtt navigator access. r=kinetik This is the fix for the "navigator is not defined" issue rebased around upstream's changes from bug 1167492 which caused timeouts in gecko tests. --- dom/media/webvtt/vtt.jsm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dom/media/webvtt/vtt.jsm b/dom/media/webvtt/vtt.jsm index b857afa0a5ec..173a93fb28fd 100644 --- a/dom/media/webvtt/vtt.jsm +++ b/dom/media/webvtt/vtt.jsm @@ -8,7 +8,7 @@ this.EXPORTED_SYMBOLS = ["WebVTT"]; * Code below is vtt.js the JS WebVTT implementation. * Current source code can be found at http://github.com/mozilla/vtt.js * - * Code taken from commit 364c6b951a07306848a706d1d03c2a6ae942517d + * Code taken from commit 58d092419a1ee84e574ce2ba18bcbf92356fcb21 */ /** * Copyright 2013 vtt.js Contributors @@ -726,7 +726,8 @@ this.EXPORTED_SYMBOLS = ["WebVTT"]; // Constructs the computed display state of the cue (a div). Places the div // into the overlay which should be a block level element (usually a div). function CueStyleBox(window, cue, styleOptions) { - var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); + var isIE8 = (typeof navigator !== "undefined") && + (/MSIE\s8\.0/).test(navigator.userAgent); var color = "rgba(255, 255, 255, 1)"; var backgroundColor = "rgba(0, 0, 0, 0.8)"; From f9e576a222409a9d21f5cd1e9fe5c68fbeecc791 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 18 Sep 2015 17:13:33 -0700 Subject: [PATCH 054/102] Bug 1206308 - include nsprpub and zlib for Windows, plus minor enhancements to make-source-package.sh, r=sstangl --HG-- extra : rebase_source : d109e313cbd1a6baaac1e215a30c8d38f858a822 extra : amend_source : ef998f30851ecba93f5853f8bdb1eb39c3d22b9b --- js/src/make-source-package.sh | 55 ++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/js/src/make-source-package.sh b/js/src/make-source-package.sh index cb79da66695b..e4ec85aa35e2 100755 --- a/js/src/make-source-package.sh +++ b/js/src/make-source-package.sh @@ -1,10 +1,31 @@ -#!/bin/sh +#!/bin/bash # Find out ASAP if some command breaks here, because we're copying a lot of # files we don't actually maintain ourselves, and requirements could easily be # broken. set -e +: ${MAKE:=make} +: ${MKDIR:=mkdir} +: ${TAR:=tar} +: ${SRCDIR:=$(cd $(dirname $0); pwd 2>/dev/null)} +: ${MOZJS_NAME:=mozjs} +: ${DIST:=/tmp/mozjs-src-pkg} + +if [[ -f "$SRCDIR/../../config/milestone.txt" ]]; then + MILESTONE="$(tail -1 $SRCDIR/../../config/milestone.txt)" + IFS=. read -a VERSION < <(echo "$MILESTONE") + MOZJS_MAJOR_VERSION=${MOZJS_MAJOR_VERSION:-${VERSION[0]}} + MOZJS_MINOR_VERSION=${MOZJS_MINOR_VERSION:-${VERSION[1]}} + MOZJS_PATCH_VERSION=${MOZJS_PATCH_VERSION:-${VERSION[2]}} +fi + +cmd=${1:-build} +pkg="${MOZJS_NAME}-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" +pkgpath=${pkg%.tar*} +tgtpath=${DIST}/${pkgpath} +taropts="-jcf" + # need these environment vars: echo "Environment:" echo " MAKE = $MAKE" @@ -12,16 +33,12 @@ echo " MKDIR = $MKDIR" echo " TAR = $TAR" echo " DIST = $DIST" echo " SRCDIR = $SRCDIR" +echo " MOZJS_NAME = $MOZJS_NAME" echo " MOZJS_MAJOR_VERSION = $MOZJS_MAJOR_VERSION" echo " MOZJS_MINOR_VERSION = $MOZJS_MINOR_VERSION" echo " MOZJS_PATCH_VERSION = $MOZJS_PATCH_VERSION" echo " MOZJS_ALPHA = $MOZJS_ALPHA" - -cmd=${1:-build} -pkg="mozjs-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" -pkgpath=${pkg%.tar*} -tgtpath=${DIST}/${pkgpath} -taropts="-jcf" +echo "" TOPSRCDIR=${SRCDIR}/../.. @@ -31,6 +48,9 @@ case $cmd in rm -rf ${pkg} ${tgtpath} ;; "build") + echo -n "Press enter to build $pkg> " + read + # Ensure that the configure script is newer than the configure.in script. if [ ${SRCDIR}/configure.in -nt ${SRCDIR}/configure ]; then echo "error: js/src/configure is out of date. Please regenerate before packaging." >&2 @@ -50,10 +70,8 @@ case $cmd in # copy main moz.build and Makefile.in cp -t ${tgtpath} -dRp ${TOPSRCDIR}/Makefile.in ${TOPSRCDIR}/moz.build - # copy a nspr file used by the build system - ${MKDIR} -p ${tgtpath}/nsprpub/config - cp -t ${tgtpath}/nsprpub/config -dRp \ - ${TOPSRCDIR}/nsprpub/config/make-system-wrappers.pl + # copy nspr. + cp -t ${tgtpath} -dRP ${SRCDIR}/../../nsprpub # copy build and config directory. cp -t ${tgtpath} -dRp ${TOPSRCDIR}/build ${TOPSRCDIR}/config @@ -80,8 +98,8 @@ case $cmd in ${MKDIR} -p ${tgtpath}/testing cp -t ${tgtpath}/testing -dRp \ ${TOPSRCDIR}/testing/mozbase - ${MKDIR} -p ${tgtpath}/modules/zlib - cp -t ${tgtpath}/modules/zlib -dRp \ + ${MKDIR} -p ${tgtpath}/modules + cp -t ${tgtpath}/modules -dRp \ ${TOPSRCDIR}/modules/zlib/src ${MKDIR} -p ${tgtpath}/layout/tools/reftest cp -t ${tgtpath}/layout/tools/reftest -dRp \ @@ -94,6 +112,7 @@ case $cmd in cp -t ${tgtpath}/mozglue -dRp \ ${TOPSRCDIR}/mozglue/build \ ${TOPSRCDIR}/mozglue/crt \ + ${TOPSRCDIR}/mozglue/misc \ ${TOPSRCDIR}/mozglue/moz.build ${MKDIR} -p ${tgtpath}/memory cp -t ${tgtpath}/memory -dRp \ @@ -108,7 +127,7 @@ case $cmd in find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f # copy or create INSTALL - if [ -e {DIST}/INSTALL ]; then + if [ -e ${DIST}/INSTALL ]; then cp -t ${tgtpath} ${DIST}/INSTALL else cat <${tgtpath}/INSTALL @@ -121,8 +140,10 @@ be run over the binaries before deploying them. Building with default options may be performed as follows: cd js/src - ./configure - make + mkdir obj + cd obj + ../configure + make # or mozmake on Windows INSTALL_EOF fi @@ -158,7 +179,7 @@ README_EOF # Roll the tarball ${TAR} $taropts ${DIST}/../${pkg} -C ${DIST} ${pkgpath} - echo "done." + echo "Wrote $(cd ${DIST}/..; echo $PWD)/${pkg}" ;; *) echo "Unrecognized command: $cmd" From f619310a0746ee31f76be3df4c665245fb21155b Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 1 Dec 2015 15:33:07 -0800 Subject: [PATCH 055/102] Backed out 6 changesets (bug 1214812) for kk emulator test bustage Backed out changeset b36710809f0c (bug 1214812) Backed out changeset a3b5c66f34e4 (bug 1214812) Backed out changeset df6d3dc3fca9 (bug 1214812) Backed out changeset 9f94be2a3c1d (bug 1214812) Backed out changeset 1eaf9f30b00d (bug 1214812) Backed out changeset 902b985b3273 (bug 1214812) --HG-- extra : commitid : LNisZGOtv0q --- testing/mozbase/mozdevice/mozdevice/adb.py | 80 ++++--------- .../mozdevice/mozdevice/adb_android.py | 109 +----------------- .../mozdevice/mozdevice/devicemanagerADB.py | 94 ++++----------- 3 files changed, 53 insertions(+), 230 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/adb.py b/testing/mozbase/mozdevice/mozdevice/adb.py index a437f51b5286..0e93db3a5e05 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -2,6 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. +from abc import ABCMeta, abstractmethod import os import posixpath import re @@ -10,8 +11,6 @@ import tempfile import time import traceback -from abc import ABCMeta, abstractmethod - class ADBProcess(object): """ADBProcess encapsulates the data related to executing the adb process.""" @@ -536,58 +535,50 @@ class ADBDevice(ADBCommand): self._have_su = False self._have_android_su = False + uid = 'uid=0' + cmd_id = 'LD_LIBRARY_PATH=/vendor/lib:/system/lib id' + # Is shell already running as root? # Catch exceptions due to the potential for segfaults # calling su when using an improperly rooted device. + try: + if self.shell_output("id").find(uid) != -1: + self._have_root_shell = True + except ADBError: + self._logger.debug("Check for root shell failed") - # Note this check to see if adbd is running is performed on - # the device in the state it exists in when the ADBDevice is - # initialized. It may be the case that it has been manipulated - # since its last boot and that its current state does not - # match the state the device will have immediately after a - # reboot. For example, if adb root was called manually prior - # to ADBDevice being initialized, then self._have_root_shell - # will not reflect the state of the device after it has been - # rebooted again. Therefore this check will need to be - # performed again after a reboot. - - self._check_adb_root(timeout=timeout) - - uid = 'uid=0' # Do we have a 'Superuser' sh like su? try: - if self.shell_output("su -c id", timeout=timeout).find(uid) != -1: + if self.shell_output("su -c '%s'" % cmd_id).find(uid) != -1: self._have_su = True - self._logger.info("su -c supported") except ADBError: - self._logger.debug("Check for su -c failed") + self._logger.debug("Check for su failed") # Do we have Android's su? try: - if self.shell_output("su 0 id", timeout=timeout).find(uid) != -1: + if self.shell_output("su 0 id").find(uid) != -1: self._have_android_su = True - self._logger.info("su 0 supported") except ADBError: - self._logger.debug("Check for su 0 failed") + self._logger.debug("Check for Android su failed") self._mkdir_p = None # Force the use of /system/bin/ls or /system/xbin/ls in case # there is /sbin/ls which embeds ansi escape codes to colorize # the output. Detect if we are using busybox ls. We want each # entry on a single line and we don't want . or .. - if self.shell_bool("/system/bin/ls /", timeout=timeout): + if self.shell_bool("/system/bin/ls /"): self._ls = "/system/bin/ls" - elif self.shell_bool("/system/xbin/ls /", timeout=timeout): + elif self.shell_bool("/system/xbin/ls /"): self._ls = "/system/xbin/ls" else: raise ADBError("ADBDevice.__init__: ls not found") try: - self.shell_output("%s -1A /" % self._ls, timeout=timeout) + self.shell_output("%s -1A /" % self._ls) self._ls += " -1A" except ADBError: self._ls += " -a" # Do we have cp? - self._have_cp = self.shell_bool("type cp", timeout=timeout) + self._have_cp = self.shell_bool("type cp") self._logger.debug("ADBDevice: %s" % self.__dict__) @@ -624,28 +615,6 @@ class ADBDevice(ADBCommand): raise ValueError("Unable to get device serial") - def _check_adb_root(self, timeout=None): - self._have_root_shell = False - uid = 'uid=0' - # Is shell already running as root? - try: - if self.shell_output("id", timeout=timeout).find(uid) != -1: - self._have_root_shell = True - self._logger.info("adbd running as root") - except ADBError: - self._logger.debug("Check for root shell failed") - - # Do we need to run adb root to get a root shell? - try: - if (not self._have_root_shell and - self.command_output( - ["root"], - timeout=timeout).find("cannot run as root") == -1): - self._have_root_shell = True - self._logger.info("adbd restarted as root") - except ADBError: - self._logger.debug("Check for root adbd failed") - @staticmethod def _escape_command_line(cmd): @@ -984,15 +953,15 @@ class ADBDevice(ADBCommand): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. """ - if root and not self._have_root_shell: - # If root was requested and we do not already have a root - # shell, then use the appropriate version of su to invoke - # the shell cmd. Prefer Android's su version since it may - # falsely report support for su -c. - if self._have_android_su: - cmd = "su 0 %s" % cmd + if root: + ld_library_path='LD_LIBRARY_PATH=/vendor/lib:/system/lib' + cmd = '%s %s' % (ld_library_path, cmd) + if self._have_root_shell: + pass elif self._have_su: cmd = "su -c \"%s\"" % cmd + elif self._have_android_su: + cmd = "su 0 \"%s\"" % cmd else: raise ADBRootError('Can not run command %s as root!' % cmd) @@ -1891,7 +1860,6 @@ class ADBDevice(ADBCommand): # 'wait-for-device' arguments which is an error in newer # versions of adb. self.command_output([], timeout=timeout) - self._check_adb_root(timeout=timeout) return self.is_device_ready(timeout=timeout) @abstractmethod diff --git a/testing/mozbase/mozdevice/mozdevice/adb_android.py b/testing/mozbase/mozdevice/mozdevice/adb_android.py index 1b6d55af5375..29f323bbb904 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb_android.py +++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py @@ -6,8 +6,6 @@ import os import re import time -from abc import ABCMeta - import version_codes from adb import ADBDevice, ADBError @@ -26,100 +24,6 @@ class ADBAndroid(ADBDevice): if adbdevice.process_exist("org.mozilla.fennec"): print "Fennec is running" """ - __metaclass__ = ABCMeta - - def __init__(self, - device=None, - adb='adb', - adb_host=None, - adb_port=None, - test_root='', - logger_name='adb', - timeout=300, - verbose=False, - device_ready_retry_wait=20, - device_ready_retry_attempts=3): - """Initializes the ADBAndroid object. - - :param device: When a string is passed, it is interpreted as the - device serial number. This form is not compatible with - devices containing a ":" in the serial; in this case - ValueError will be raised. - When a dictionary is passed it must have one or both of - the keys "device_serial" and "usb". This is compatible - with the dictionaries in the list returned by - ADBHost.devices(). If the value of device_serial is a - valid serial not containing a ":" it will be used to - identify the device, otherwise the value of the usb key, - prefixed with "usb:" is used. - If None is passed and there is exactly one device attached - to the host, that device is used. If there is more than one - device attached, ValueError is raised. If no device is - attached the constructor will block until a device is - attached or the timeout is reached. - :type device: dict, str or None - :param adb_host: host of the adb server to connect to. - :type adb_host: str or None - :param adb_port: port of the adb server to connect to. - :type adb_port: integer or None - :param str logger_name: logging logger name. Defaults to 'adb'. - :param integer device_ready_retry_wait: number of seconds to wait - between attempts to check if the device is ready after a - reboot. - :param integer device_ready_retry_attempts: number of attempts when - checking if a device is ready. - - :raises: * ADBError - * ADBTimeoutError - * ValueError - """ - ADBDevice.__init__(self, device=device, adb=adb, - adb_host=adb_host, adb_port=adb_port, - test_root=test_root, - logger_name=logger_name, timeout=timeout, - verbose=verbose, - device_ready_retry_wait=device_ready_retry_wait, - device_ready_retry_attempts=device_ready_retry_attempts) - # https://source.android.com/devices/tech/security/selinux/index.html - # setenforce - # usage: setenforce [ Enforcing | Permissive | 1 | 0 ] - # getenforce returns either Enforcing or Permissive - - try: - self.selinux = True - if self.shell_output('getenforce', timeout=timeout) != 'Permissive': - self._logger.info('Setting SELinux Permissive Mode') - self.shell_output("setenforce Permissive", timeout=timeout, root=True) - except ADBError: - self.selinux = False - - def reboot(self, timeout=None): - """Reboots the device. - - :param timeout: optional integer specifying the maximum time in - seconds for any spawned adb process to complete before - throwing an ADBTimeoutError. - This timeout is per adb call. The total time spent - may exceed this value. If it is not specified, the value - set in the ADB constructor is used. - :raises: * ADBTimeoutError - * ADBError - - reboot() reboots the device, issues an adb wait-for-device in order to - wait for the device to complete rebooting, then calls is_device_ready() - to determine if the device has completed booting. - - If the device supports running adbd as root, adbd will be - restarted running as root. Then, if the device supports - SELinux, setenforce Permissive will be called to change - SELinux to permissive. This must be done after adbd is - restarted in order for the SELinux Permissive setting to - persist. - - """ - ready = ADBDevice.reboot(self, timeout=timeout) - self._check_adb_root(timeout=timeout) - return ready # Informational methods @@ -195,15 +99,10 @@ class ADBAndroid(ADBDevice): failure = "Device state: %s" % state success = False else: - if (self.selinux and - self.shell_output('getenforce', - timeout=timeout) != 'Permissive'): - self._logger.info('Setting SELinux Permissive Mode') - self.shell_output("setenforce Permissive", timeout=timeout, root=True) - if self.is_dir(ready_path, timeout=timeout, root=True): - self.rmdir(ready_path, timeout=timeout, root=True) - self.mkdir(ready_path, timeout=timeout, root=True) - self.rmdir(ready_path, timeout=timeout, root=True) + if self.is_dir(ready_path, timeout=timeout): + self.rmdir(ready_path, timeout=timeout) + self.mkdir(ready_path, timeout=timeout) + self.rmdir(ready_path, timeout=timeout) # Invoke the pm list commands to see if it is up and # running. for pm_list_cmd in pm_list_commands: diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index 35d6d8239ce9..8b3bddcff801 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -23,10 +23,8 @@ class DeviceManagerADB(DeviceManager): port. """ - _haveRootShell = None - _haveSu = None - _suModifier = None - _lsModifier = None + _haveRootShell = False + _haveSu = False _useZip = False _logcatNeedsRoot = False _pollingInterval = 0.01 @@ -72,8 +70,6 @@ class DeviceManagerADB(DeviceManager): if autoconnect: self.connect() - self._detectLsModifier() - def connect(self): if not self.connected: # try to connect to the device over tcp/ip if we have a hostname @@ -105,22 +101,17 @@ class DeviceManagerADB(DeviceManager): # always. :( # If requested to run as root, check that we can actually do that - if root: - if self._haveRootShell is None and self._haveSu is None: - self._checkForRoot() - if not self._haveRootShell and not self._haveSu: - raise DMError( - "Shell command '%s' requested to run as root but root " - "is not available on this device. Root your device or " - "refactor the test/harness to not require root." % - self._escapedCommandLine(cmd)) + if root and not self._haveRootShell and not self._haveSu: + raise DMError("Shell command '%s' requested to run as root but root " + "is not available on this device. Root your device or " + "refactor the test/harness to not require root." % + self._escapedCommandLine(cmd)) # Getting the return code is more complex than you'd think because adb # doesn't actually return the return code from a process, so we have to # capture the output to get it - if root and self._haveSu: - cmdline = "su %s \"%s\"" % (self._suModifier, - self._escapedCommandLine(cmd)) + if root and not self._haveRootShell: + cmdline = "su -c \"%s\"" % self._escapedCommandLine(cmd) else: cmdline = self._escapedCommandLine(cmd) cmdline += "; echo $?" @@ -268,8 +259,7 @@ class DeviceManagerADB(DeviceManager): mozfile.remove(tmpDir) def dirExists(self, remotePath): - data = self._runCmd(["shell", "ls", self._lsModifier, remotePath + '/'], - timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", "-a", remotePath + '/'], timeout=self.short_timeout).output if len(data) == 1: res = data[0] @@ -278,8 +268,7 @@ class DeviceManagerADB(DeviceManager): return True def fileExists(self, filepath): - data = self._runCmd(["shell", "ls", self._lsModifier, filepath], - timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", "-a", filepath], timeout=self.short_timeout).output if len(data) == 1: foundpath = data[0].decode('utf-8').rstrip() if foundpath == filepath: @@ -303,8 +292,7 @@ class DeviceManagerADB(DeviceManager): self._checkCmd(["shell", "dd", "if=%s" % source, "of=%s" % destination]) def listFiles(self, rootdir): - data = self._runCmd(["shell", "ls", self._lsModifier, rootdir], - timeout=self.short_timeout).output + data = self._runCmd(["shell", "ls", "-a", rootdir], timeout=self.short_timeout).output data[:] = [item.rstrip('\r\n') for item in data] if (len(data) == 1): if (data[0] == rootdir): @@ -317,8 +305,6 @@ class DeviceManagerADB(DeviceManager): return [] if (data[0].find("opendir failed") != -1): return [] - if (data[0].find("Device or resource busy") != -1): - return [] return data def getProcessList(self): @@ -676,13 +662,6 @@ class DeviceManagerADB(DeviceManager): raise DMError("unable to connect to device") def _checkForRoot(self): - self._haveRootShell = False - self._haveSu = False - # If requested to attempt to run adbd as root, do so before - # checking whether adbs is running as root. - if self._runAdbAsRoot: - self._adb_root() - # Check whether we _are_ root by default (some development boards work # this way, this is also the result of some relatively rare rooting # techniques) @@ -694,30 +673,22 @@ class DeviceManagerADB(DeviceManager): # if root shell is not available, check if 'su' can be used to gain # root - def su_id(su_modifier, timeout): - proc = self._runCmd(["shell", "su", su_modifier, "id"], - timeout=timeout) + proc = self._runCmd(["shell", "su", "-c", "id"], timeout=self.short_timeout) - # wait for response for maximum of 15 seconds, in case su - # prompts for a password or triggers the Android SuperUser - # prompt - start_time = time.time() - retcode = None - while (time.time() - start_time) <= 15 and retcode is None: - retcode = proc.poll() - if retcode is None: # still not terminated, kill - proc.kill() + # wait for response for maximum of 15 seconds, in case su prompts for a + # password or triggers the Android SuperUser prompt + start_time = time.time() + retcode = None + while (time.time() - start_time) <= 15 and retcode is None: + retcode = proc.poll() + if retcode is None: # still not terminated, kill + proc.kill() - if proc.output and 'uid=0(root)' in proc.output[0]: - return True - return False - - if su_id('0', self.short_timeout): + if proc.output and 'uid=0(root)' in proc.output[0]: self._haveSu = True - self._suModifier = '0' - elif su_id('-c', self.short_timeout): - self._haveSu = True - self._suModifier = '-c' + + if self._runAdbAsRoot: + self._adb_root() def _isUnzipAvailable(self): data = self._runCmd(["shell", "unzip"]).output @@ -758,18 +729,3 @@ class DeviceManagerADB(DeviceManager): self._checkCmd(["wait-for-device"]) if self.processInfo("adbd")[2] != "root": raise DMError("We tried rebooting adbd as root, however, it failed.") - - def _detectLsModifier(self): - if self._lsModifier is None: - # Check if busybox -1A is required in order to get one - # file per line. - output = self._runCmd(["shell", "ls", "-1A" "/"], - timeout=self.short_timeout).output - output = ' '.join(output) - if "Unknown option '-1'. Aborting." in output: - self._lsModifier = "-a" - elif "-1A/: No such file or directory" in output: - self._lsModifier = "-a" - else: - self._lsModifier = "-1A" - From 9191c537d38204d62cbe7080cd8fd1dd0d857b03 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 1 Dec 2015 11:05:46 +1100 Subject: [PATCH 056/102] Bug 1229134: Check that memory allocation actually succeeded. r=gerald --- .../frameworks/av/media/libstagefright/MPEG4Extractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index 0890c4c12b30..b83e758ef099 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -1650,7 +1650,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } sp buffer = new (fallible) ABuffer(chunk_data_size); - if (!buffer.get()) { + if (!buffer.get() || !buffer->data()) { return -ENOMEM; } @@ -1922,7 +1922,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; } sp buffer = new (fallible) ABuffer(chunk_data_size + 1); - if (!buffer.get()) { + if (!buffer.get() || !buffer->data()) { return -ENOMEM; } if (mDataSource->readAt( From 87d0bbae4371baeabd956137d64c2f1b9ba04ef7 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 1 Dec 2015 11:37:42 +1100 Subject: [PATCH 057/102] Bug 1226842: Error rather than asserting when encountering error in sample table. r=gerald --- .../av/media/libstagefright/SampleIterator.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp index 2e8c7c251acd..37bb2b7a5d73 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp @@ -83,7 +83,9 @@ status_t SampleIterator::seekTo(uint32_t sampleIndex) { } } - CHECK(sampleIndex < mStopChunkSampleIndex); + if (sampleIndex >= mStopChunkSampleIndex) { + return ERROR_MALFORMED; + } uint32_t chunk = (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk @@ -116,11 +118,14 @@ status_t SampleIterator::seekTo(uint32_t sampleIndex) { } } - CHECK(mCurrentChunkSampleSizes.size() == mSamplesPerChunk); + if (mCurrentChunkSampleSizes.size() != mSamplesPerChunk) { + return ERROR_MALFORMED; + } uint32_t chunkRelativeSampleIndex = (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; + // This can never happen unless % operator is buggy. CHECK(chunkRelativeSampleIndex < mSamplesPerChunk); mCurrentSampleOffset = mCurrentChunkOffset; From b9f3760d8af5f39057615ce025f9e9e748dcc6c5 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 1 Dec 2015 19:52:43 +1100 Subject: [PATCH 058/102] Bug 1229299: Use tail dispatch to notify the mirror of new value. r=bholley This ensures that tasks are run in the proper order. --- xpcom/threads/StateMirroring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpcom/threads/StateMirroring.h b/xpcom/threads/StateMirroring.h index 35e04f11005b..bcb65fea381d 100644 --- a/xpcom/threads/StateMirroring.h +++ b/xpcom/threads/StateMirroring.h @@ -137,7 +137,7 @@ private: MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn()); MOZ_ASSERT(!mMirrors.Contains(aMirror)); mMirrors.AppendElement(aMirror); - aMirror->OwnerThread()->Dispatch(MakeNotifier(aMirror), AbstractThread::DontAssertDispatchSuccess); + aMirror->OwnerThread()->DispatchStateChange(MakeNotifier(aMirror)); } void RemoveMirror(AbstractMirror* aMirror) override From 0c156b26dcf13691d8b5ca929f18b9fdb39f9d93 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 1 Dec 2015 15:53:00 -0800 Subject: [PATCH 059/102] Bug 1229579 - Make class members properly XDR-able. (r=billm) --- js/src/jit-test/tests/xdr/classes.js | 11 +++++++++++ js/src/jsscript.cpp | 10 ++++++++++ js/src/vm/Xdr.h | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/xdr/classes.js diff --git a/js/src/jit-test/tests/xdr/classes.js b/js/src/jit-test/tests/xdr/classes.js new file mode 100644 index 000000000000..e91da04eb0e3 --- /dev/null +++ b/js/src/jit-test/tests/xdr/classes.js @@ -0,0 +1,11 @@ +load(libdir + 'bytecode-cache.js'); +load(libdir + 'class.js'); + +if (!classesEnabled()) + quit(); + +var test = "new class extends class { } { constructor() { super(); } }()"; +evalWithCache(test, { assertEqBytecode : true }); + +var test = "new class { method() { super.toString(); } }().method()"; +evalWithCache(test, { assertEqBytecode : true }); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 742374e57287..55bfeec18482 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -622,6 +622,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript HasLazyScript, HasNonSyntacticScope, HasInnerFunctions, + NeedsHomeObject, + IsDerivedClassConstructor, }; uint32_t length, lineno, column, nslots; @@ -761,6 +763,10 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript scriptBits |= (1 << HasNonSyntacticScope); if (script->hasInnerFunctions()) scriptBits |= (1 << HasInnerFunctions); + if (script->needsHomeObject()) + scriptBits |= (1 << NeedsHomeObject); + if (script->isDerivedClassConstructor()) + scriptBits |= (1 << IsDerivedClassConstructor); } if (!xdr->codeUint32(&prologueLength)) @@ -901,6 +907,10 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript script->hasNonSyntacticScope_ = true; if (scriptBits & (1 << HasInnerFunctions)) script->hasInnerFunctions_ = true; + if (scriptBits & (1 << NeedsHomeObject)) + script->needsHomeObject_ = true; + if (scriptBits & (1 << IsDerivedClassConstructor)) + script->isDerivedClassConstructor_ = true; if (scriptBits & (1 << IsLegacyGenerator)) { MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator))); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 39aad3900193..370839a0e894 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 324; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 325; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); From a0a2e12a735fb2114dfa9cd19e5f5e898ed8a9ca Mon Sep 17 00:00:00 2001 From: Ralph Giles Date: Tue, 1 Dec 2015 17:33:00 -0800 Subject: [PATCH 060/102] Bug 1229612 - Update rust mp4parse to v0.1.5. r=kinetik Adds query functions for track metadata. --- media/libstagefright/binding/MP4Metadata.rs | 372 +++++++++++++----- media/libstagefright/binding/capi.rs | 162 +++++++- .../libstagefright/binding/include/mp4parse.h | 40 ++ 3 files changed, 469 insertions(+), 105 deletions(-) diff --git a/media/libstagefright/binding/MP4Metadata.rs b/media/libstagefright/binding/MP4Metadata.rs index 0cf06107d619..a16c1a36ce09 100644 --- a/media/libstagefright/binding/MP4Metadata.rs +++ b/media/libstagefright/binding/MP4Metadata.rs @@ -8,7 +8,6 @@ mod byteorder; // 'extern crate' upstream. use byteorder::ReadBytesExt; use std::error::Error as ErrorTrait; // For Err(e) => e.description(). use std::io::{Read, BufRead, Take}; -use std::io::Cursor; use std::cmp; use std::fmt; @@ -97,7 +96,7 @@ struct MovieHeaderBox { } /// Track header box 'tkhd' -#[derive(Debug)] +#[derive(Debug, Clone)] struct TrackHeaderBox { name: FourCC, size: u64, @@ -204,32 +203,39 @@ struct SampleDescriptionBox { } #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Clone)] enum SampleEntry { - Audio { - data_reference_index: u16, - channelcount: u16, - samplesize: u16, - samplerate: u32, - esds: ES_Descriptor, - }, - Video { - data_reference_index: u16, - width: u16, - height: u16, - avcc: AVCDecoderConfigurationRecord, - }, + Audio(AudioSampleEntry), + Video(VideoSampleEntry), + Unknown, +} + +#[derive(Debug, Clone)] +struct AudioSampleEntry { + data_reference_index: u16, + channelcount: u16, + samplesize: u16, + samplerate: u32, + esds: ES_Descriptor, +} + +#[derive(Debug, Clone)] +struct VideoSampleEntry { + data_reference_index: u16, + width: u16, + height: u16, + avcc: AVCDecoderConfigurationRecord, } #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Clone)] struct AVCDecoderConfigurationRecord { data: Vec, } #[allow(non_camel_case_types)] #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Clone)] struct ES_Descriptor { data: Vec, } @@ -237,6 +243,7 @@ struct ES_Descriptor { /// Internal data structures. #[derive(Debug)] pub struct MediaContext { + timescale: Option, /// Tracks found in the file. tracks: Vec, /// Print boxes and other info as parsing proceeds. For debugging. @@ -246,10 +253,12 @@ pub struct MediaContext { impl MediaContext { pub fn new() -> MediaContext { MediaContext { + timescale: None, tracks: Vec::new(), trace: false, } } + pub fn trace(&mut self, on: bool) { self.trace = on; } @@ -265,13 +274,50 @@ macro_rules! log { #[derive(Debug)] enum TrackType { - Video, Audio, + Video, + Unknown, } +#[derive(Debug, Copy, Clone)] +struct MediaTimeScale(u64); /// The media's global (mvhd) timescale. + +#[derive(Debug, Copy, Clone)] +struct MediaScaledTime(u64); /// A time scaled by the media's global (mvhd) timescale. + +#[derive(Debug, Copy, Clone)] +struct TrackTimeScale(u64, usize); /// The track's local (mdhd) timescale. + +#[derive(Debug, Copy, Clone)] +struct TrackScaledTime(u64, usize); /// A time scaled by the track's local (mdhd) timescale. + #[derive(Debug)] struct Track { track_type: TrackType, + empty_duration: Option, + media_time: Option, + timescale: Option, + duration: Option, + track_id: Option, + mime_type: String, + data: Option, + tkhd: Option, // TODO(kinetik): find a nicer way to export this. +} + +impl Track { + fn new() -> Track { + Track { + track_type: TrackType::Unknown, + empty_duration: None, + media_time: None, + timescale: None, + duration: None, + track_id: None, + mime_type: String::new(), + data: None, + tkhd: None, + } + } } /// Read and parse a box header. @@ -332,26 +378,26 @@ fn skip_remaining_box_content (src: &mut T, header: &BoxHeader) -> R } /// Helper to construct a Take over the contents of a box. -fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> { +fn limit<'a, T: BufRead>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> { f.take(h.size - h.offset) } -/// Helper to construct a Cursor over the contents of a box. -fn recurse(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> Result<()> { - log!(context, "{:?} -- recursing", h); - // FIXME: I couldn't figure out how to do this without copying. - // We use Seek on the Read we return in skip_box_content, but - // that trait isn't implemented for a Take like our limit() - // returns. Slurping the buffer and wrapping it in a Cursor - // functions as a work around. - let buf: Vec = f - .bytes() - .map(|u| u.unwrap()) - .collect(); - let mut content = Cursor::new(buf); +fn driver(f: &mut T, context: &mut MediaContext, action: F) -> Result<()> + where F: Fn(&mut MediaContext, BoxHeader, &mut Take<&mut T>) -> Result<()> { loop { - match read_box(&mut content, context) { - Ok(_) => {}, + let r = read_box_header(f).and_then(|h| { + let mut content = limit(f, &h); + let r = action(context, h, &mut content); + if let Ok(_) = r { + // TODO(kinetik): can check this for "non-fatal" errors (e.g. EOF) too. + log!(context, "{} content bytes left", content.limit()); + assert!(content.limit() == 0); + log!(context, "read_box context: {:?}", context); + } + r + }); + match r { + Ok(_) => { }, Err(Error::UnexpectedEOF) => { // byteorder returns EOF at the end of the buffer. // This isn't an error for us, just an signal to @@ -369,13 +415,11 @@ fn recurse(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> Res }, Err(Error::Io(e)) => { log!(context, "I/O Error '{:?}' reading box: {:?}", - e.kind(), e.description()); + e.kind(), e.description()); return Err(Error::Io(e)); }, } } - assert!(content.position() == h.size - h.offset); - log!(context, "{:?} -- end", h); Ok(()) } @@ -383,36 +427,183 @@ fn recurse(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> Res /// /// Metadata is accumulated in the passed-through MediaContext struct, /// which can be examined later. -pub fn read_box(f: &mut T, context: &mut MediaContext) -> Result<()> { - read_box_header(f).and_then(|h| { - let mut content = limit(f, &h); +pub fn read_mp4(f: &mut T, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { match &h.name.0 { b"ftyp" => { let ftyp = try!(read_ftyp(&mut content, &h)); log!(context, "{:?}", ftyp); }, - b"moov" => try!(recurse(&mut content, &h, context)), + b"moov" => try!(read_moov(&mut content, &h, context)), + _ => { + // Skip the contents of unknown chunks. + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) + }) +} + +fn read_moov(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { b"mvhd" => { let mvhd = try!(read_mvhd(&mut content, &h)); + context.timescale = Some(MediaTimeScale(mvhd.timescale as u64)); log!(context, " {:?}", mvhd); }, - b"trak" => try!(recurse(&mut content, &h, context)), + b"trak" => { + context.tracks.push(Track::new()); + try!(read_trak(&mut content, &h, context)); + }, + _ => { + // Skip the contents of unknown chunks. + log!(context, "{:?} (skipped)", h); + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) + }) +} + +fn read_trak(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { b"tkhd" => { let tkhd = try!(read_tkhd(&mut content, &h)); + if let Some(track) = context.tracks.last_mut() { + track.track_id = Some(tkhd.track_id); + track.tkhd = Some(tkhd.clone()) + } else { + return Err(Error::InvalidData); + } log!(context, " {:?}", tkhd); }, - b"edts" => try!(recurse(&mut content, &h, context)), + b"edts" => try!(read_edts(&mut content, &h, context)), + b"mdia" => try!(read_mdia(&mut content, &h, context)), + _ => { + // Skip the contents of unknown chunks. + log!(context, "{:?} (skipped)", h); + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) // and_then needs a Result to return. + }) +} + +fn read_edts(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { b"elst" => { let elst = try!(read_elst(&mut content, &h)); + let mut empty_duration = 0; + let track_idx = context.tracks.len() - 1; + if let Some(track) = context.tracks.last_mut() { + let mut idx = 0; + if elst.edits.len() > 2 { + return Err(Error::Unsupported); + } + if elst.edits[idx].media_time == -1 { + empty_duration = elst.edits[0].segment_duration; + idx += 1; + } + track.empty_duration = Some(MediaScaledTime(empty_duration)); + if elst.edits[idx].media_time < 0 { + return Err(Error::InvalidData); + } + track.media_time = Some(TrackScaledTime(elst.edits[idx].media_time as u64, + track_idx)); + } else { + return Err(Error::InvalidData); + } log!(context, " {:?}", elst); }, - b"mdia" => try!(recurse(&mut content, &h, context)), + _ => { + // Skip the contents of unknown chunks. + log!(context, "{:?} (skipped)", h); + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) + }) +} + +fn read_mdia(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { b"mdhd" => { let mdhd = try!(read_mdhd(&mut content, &h)); + let track_idx = context.tracks.len() - 1; + if let Some(track) = context.tracks.last_mut() { + track.duration = Some(TrackScaledTime(mdhd.duration, track_idx)); + track.timescale = Some(TrackTimeScale(mdhd.timescale as u64, + track_idx)); + } else { + return Err(Error::InvalidData); + } log!(context, " {:?}", mdhd); }, - b"minf" => try!(recurse(&mut content, &h, context)), - b"stbl" => try!(recurse(&mut content, &h, context)), + b"hdlr" => { + let hdlr = try!(read_hdlr(&mut content, &h)); + if let Some(track) = context.tracks.last_mut() { + match &hdlr.handler_type.0 { + b"vide" => track.track_type = TrackType::Video, + b"soun" => track.track_type = TrackType::Audio, + _ => () + } + } else { + return Err(Error::InvalidData); + } + log!(context, " {:?}", hdlr); + }, + b"minf" => try!(read_minf(&mut content, &h, context)), + _ => { + // Skip the contents of unknown chunks. + log!(context, "{:?} (skipped)", h); + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) + }) +} + +fn read_minf(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { + b"stbl" => try!(read_stbl(&mut content, &h, context)), + _ => { + // Skip the contents of unknown chunks. + log!(context, "{:?} (skipped)", h); + try!(skip_box_content(&mut content, &h)); + }, + }; + Ok(()) + }) +} + +fn read_stbl(f: &mut T, _: &BoxHeader, context: &mut MediaContext) -> Result<()> { + driver(f, context, |context, h, mut content| { + match &h.name.0 { + b"stsd" => { + if let Some(track) = context.tracks.last_mut() { + let stsd = try!(read_stsd(&mut content, &h, track)); + log!(context, " {:?}", stsd); + } else { + return Err(Error::InvalidData); + } + }, + b"stts" => { + let stts = try!(read_stts(&mut content, &h)); + log!(context, " {:?}", stts); + }, + b"stsc" => { + let stsc = try!(read_stsc(&mut content, &h)); + log!(context, " {:?}", stsc); + }, + b"stsz" => { + let stsz = try!(read_stsz(&mut content, &h)); + log!(context, " {:?}", stsz); + }, b"stco" => { let stco = try!(read_stco(&mut content, &h)); log!(context, " {:?}", stco); @@ -425,48 +616,13 @@ pub fn read_box(f: &mut T, context: &mut MediaContext) -> Result<()> let stss = try!(read_stss(&mut content, &h)); log!(context, " {:?}", stss); }, - b"stsc" => { - let stsc = try!(read_stsc(&mut content, &h)); - log!(context, " {:?}", stsc); - }, - b"stsz" => { - let stsz = try!(read_stsz(&mut content, &h)); - log!(context, " {:?}", stsz); - }, - b"stts" => { - let stts = try!(read_stts(&mut content, &h)); - log!(context, " {:?}", stts); - }, - b"hdlr" => { - let hdlr = try!(read_hdlr(&mut content, &h)); - let track_type = match &hdlr.handler_type.0 { - b"vide" => Some(TrackType::Video), - b"soun" => Some(TrackType::Audio), - _ => None - }; - // Save track types with recognized types. - match track_type { - Some(track_type) => - context.tracks.push(Track { track_type: track_type }), - None => log!(context, "unknown track type!"), - }; - log!(context, " {:?}", hdlr); - }, - b"stsd" => { - let track = &context.tracks[context.tracks.len() - 1]; - let stsd = try!(read_stsd(&mut content, &h, &track)); - log!(context, " {:?}", stsd); - }, _ => { // Skip the contents of unknown chunks. log!(context, "{:?} (skipped)", h); try!(skip_box_content(&mut content, &h)); }, }; - log!(context, "{} content bytes left", content.limit()); - assert!(content.limit() == 0); - log!(context, "read_box context: {:?}", context); - Ok(()) // and_then needs a Result to return. + Ok(()) }) } @@ -758,18 +914,19 @@ fn read_hdlr(src: &mut T, head: &BoxHeader) -> Result } /// Parse a stsd box. -fn read_stsd(src: &mut T, head: &BoxHeader, track: &Track) -> Result { +fn read_stsd(src: &mut T, head: &BoxHeader, track: &mut Track) -> Result { let (_, _) = try!(read_fullbox_extra(src)); let description_count = try!(be_u32(src)); let mut descriptions = Vec::new(); + // TODO(kinetik): check if/when more than one desc per track? do we need to support? for _ in 0..description_count { let description = match track.track_type { TrackType::Video => { let h = try!(read_box_header(src)); - // TODO(kinetik): avc3 and encv here also? - if &h.name.0 != b"avc1" { + // TODO(kinetik): encv here also? + if &h.name.0 != b"avc1" && &h.name.0 != b"avc3" { return Err(Error::Unsupported); } @@ -799,12 +956,14 @@ fn read_stsd(src: &mut T, head: &BoxHeader, track: &T try!(skip_remaining_box_content(src, head)); - SampleEntry::Video { + track.mime_type = String::from("video/avc"); + + SampleEntry::Video(VideoSampleEntry { data_reference_index: data_reference_index, width: width, height: height, avcc: avcc, - } + }) }, TrackType::Audio => { let h = try!(read_box_header(src)); @@ -840,15 +999,26 @@ fn read_stsd(src: &mut T, head: &BoxHeader, track: &T assert!(r == data.len()); let esds = ES_Descriptor { data: data }; - SampleEntry::Audio { + // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg). + track.mime_type = String::from("audio/mp4a-latm"); + + SampleEntry::Audio(AudioSampleEntry { data_reference_index: data_reference_index, channelcount: channelcount, samplesize: samplesize, samplerate: samplerate, esds: esds, - } + }) }, + TrackType::Unknown => { + SampleEntry::Unknown + } }; + if track.data.is_none() { + track.data = Some(description.clone()); + } else { + return Err(Error::InvalidData); + } descriptions.push(description); } @@ -875,6 +1045,15 @@ fn skip(src: &mut T, bytes: usize) -> Result { Ok(bytes) } +fn media_time_to_ms(time: MediaScaledTime, scale: MediaTimeScale) -> u64 { + time.0 * 1000000 / scale.0 +} + +fn track_time_to_ms(time: TrackScaledTime, scale: TrackTimeScale) -> u64 { + assert!(time.1 == scale.1); + time.0 * 1000000 / scale.0 +} + fn be_i16(src: &mut T) -> byteorder::Result { src.read_i16::() } @@ -913,7 +1092,7 @@ fn be_fourcc(src: &mut T) -> Result { #[test] fn test_read_box_header() { - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 8]; // minimal box length write!(&mut test, "test").unwrap(); // box type let mut stream = Cursor::new(test); @@ -939,8 +1118,7 @@ fn test_read_box_header_long() { #[test] fn test_read_ftyp() { - use std::io::Cursor; - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 24]; // size write!(&mut test, "ftyp").unwrap(); // type write!(&mut test, "mp42").unwrap(); // major brand @@ -964,7 +1142,7 @@ fn test_read_ftyp() { #[test] fn test_read_elst_v0() { - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 28]; // size write!(&mut test, "elst").unwrap(); // type test.extend(vec![0, 0, 0, 0]); // fullbox @@ -990,7 +1168,7 @@ fn test_read_elst_v0() { #[test] fn test_read_elst_v1() { - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 56]; // size write!(&mut test, "elst").unwrap(); // type test.extend(vec![1, 0, 0, 0]); // fullbox @@ -1020,7 +1198,7 @@ fn test_read_elst_v1() { #[test] fn test_read_mdhd_v0() { - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 32]; // size write!(&mut test, "mdhd").unwrap(); // type test.extend(vec![0, 0, 0, 0]); // fullbox @@ -1043,7 +1221,7 @@ fn test_read_mdhd_v0() { #[test] fn test_read_mdhd_v1() { - use std::io::Write; + use std::io::{Cursor, Write}; let mut test: Vec = vec![0, 0, 0, 44]; // size write!(&mut test, "mdhd").unwrap(); // type test.extend(vec![1, 0, 0, 0]); // fullbox diff --git a/media/libstagefright/binding/capi.rs b/media/libstagefright/binding/capi.rs index 8e91b7297e3a..5fdd2d4ab087 100644 --- a/media/libstagefright/binding/capi.rs +++ b/media/libstagefright/binding/capi.rs @@ -27,8 +27,41 @@ use std::io::Cursor; // Symbols we need from our rust api. use MediaContext; -use read_box; +use TrackType; +use read_mp4; use Error; +use media_time_to_ms; +use track_time_to_ms; +use SampleEntry; + +const TRACK_TYPE_H264: u32 = 0; +const TRACK_TYPE_AAC: u32 = 1; + +// These structs *must* match those declared in include/mp4parse.h. +#[repr(C)] +pub struct TrackInfo { + track_type: u32, + track_id: u32, + duration: u64, + media_time: i64, // wants to be u64? understand how elst adjustment works +} + +#[repr(C)] +pub struct TrackAudioInfo { + channels: u16, + bit_depth: u16, + sample_rate: u32, +// profile: i32, +// extended_profile: i32, // check types +} + +#[repr(C)] +pub struct TrackVideoInfo { + display_width: u32, + display_height: u32, + image_width: u16, + image_height: u16, +} /// Allocate an opaque rust-side parser context. #[no_mangle] @@ -44,7 +77,7 @@ pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) { let _ = Box::from_raw(context); } -/// Feed a buffer through `read_box()` with the given rust-side +/// Feed a buffer through `read_mp4()` with the given rust-side /// parser context, returning the number of detected tracks. /// /// This is safe to call with NULL arguments but will crash @@ -64,12 +97,10 @@ pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *cons // Parse in a subthread to catch any panics. let task = std::thread::spawn(move || { - loop { - match read_box(&mut c, &mut context) { - Ok(_) => {}, - Err(Error::UnexpectedEOF) => { break }, - Err(e) => { panic!(e); }, - } + match read_mp4(&mut c, &mut context) { + Ok(_) => {}, + Err(Error::UnexpectedEOF) => {}, + Err(e) => { panic!(e); }, } // Make sure the track count fits in an i32 so we can use // negative values for failure. @@ -79,6 +110,121 @@ pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *cons task.join().unwrap_or(-1) } +#[no_mangle] +pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut MediaContext, track: i32, info: *mut TrackInfo) -> i32 { + if context.is_null() || track < 0 || info.is_null() { + return -1; + } + + let context: &mut MediaContext = &mut *context; + + if track as usize >= context.tracks.len() { + return -1; + } + + let track = &context.tracks[track as usize]; + + (*info).track_type = match track.track_type { + TrackType::Video => TRACK_TYPE_H264, + TrackType::Audio => TRACK_TYPE_AAC, + TrackType::Unknown => return -1, + }; + + // Maybe context & track should just have a single simple is_valid() instead? + if context.timescale.is_none() || + track.timescale.is_none() || + track.empty_duration.is_none() || + track.media_time.is_none() || + track.duration.is_none() || + track.track_id.is_none() { + return -1; + } + + let empty_duration = media_time_to_ms(track.empty_duration.unwrap(), context.timescale.unwrap()); + (*info).media_time = track_time_to_ms(track.media_time.unwrap(), track.timescale.unwrap()) as i64 - empty_duration as i64; + (*info).duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap()); + (*info).track_id = track.track_id.unwrap(); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track: i32, info: *mut TrackAudioInfo) -> i32 { + if context.is_null() || track < 0 || info.is_null() { + return -1; + } + + let context: &mut MediaContext = &mut *context; + + if track as usize >= context.tracks.len() { + return -1; + } + + let track = &context.tracks[track as usize]; + + match track.track_type { + TrackType::Audio => {}, + _ => return -1, + }; + + let audio = match track.data { + Some(ref data) => data, + None => return -1, + }; + + let audio = match audio { + &SampleEntry::Audio(ref x) => x, + _ => return -1, + }; + + (*info).channels = audio.channelcount; + (*info).bit_depth = audio.samplesize; + (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut MediaContext, track: i32, info: *mut TrackVideoInfo) -> i32 { + if context.is_null() || track < 0 || info.is_null() { + return -1; + } + + let context: &mut MediaContext = &mut *context; + + if track as usize >= context.tracks.len() { + return -1; + } + + let track = &context.tracks[track as usize]; + + match track.track_type { + TrackType::Video => {}, + _ => return -1, + }; + + let video = match track.data { + Some(ref data) => data, + None => return -1, + }; + + let video = match video { + &SampleEntry::Video(ref x) => x, + _ => return -1, + }; + + if let Some(ref tkhd) = track.tkhd { + (*info).display_width = tkhd.width >> 16; // 16.16 fixed point + (*info).display_height = tkhd.height >> 16; // 16.16 fixed point + } else { + return -1 + } + (*info).image_width = video.width; + (*info).image_width = video.height; + + 0 +} + #[test] fn new_context() { let context = mp4parse_new(); diff --git a/media/libstagefright/binding/include/mp4parse.h b/media/libstagefright/binding/include/mp4parse.h index 7bd2d053f68b..cbe993a8bee1 100644 --- a/media/libstagefright/binding/include/mp4parse.h +++ b/media/libstagefright/binding/include/mp4parse.h @@ -11,11 +11,51 @@ extern "C" { struct mp4parse_state; +#define MP4PARSE_TRACK_TYPE_H264 0 // "video/avc" +#define MP4PARSE_TRACK_TYPE_AAC 1 // "audio/mp4a-latm" + +struct mp4parse_track_audio_info { + uint16_t channels; + uint16_t bit_depth; + uint32_t sample_rate; + //int32_t profile; + //int32_t extended_profile; // check types + + // TODO(kinetik): + // extra_data + // codec_specific_config +}; + +struct mp4parse_track_video_info { + uint32_t display_width; + uint32_t display_height; + uint16_t image_width; + uint16_t image_height; + + // TODO(kinetik): + // extra_data + // codec_specific_config +}; + +struct mp4parse_track_info { + uint32_t track_type; + uint32_t track_id; + uint64_t duration; + int64_t media_time; + // TODO(kinetik): crypto guff +}; + struct mp4parse_state* mp4parse_new(void); void mp4parse_free(struct mp4parse_state* state); int32_t mp4parse_read(struct mp4parse_state* state, uint8_t *buffer, size_t size); +int32_t mp4parse_get_track_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_info* track_info); + +int32_t mp4parse_get_track_audio_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_audio_info* track_info); + +int32_t mp4parse_get_track_video_info(struct mp4parse_state* state, int32_t track, struct mp4parse_track_video_info* track_info); + #ifdef __cplusplus } #endif From 572059374ecd56f79e1d0ff535c20d1b9cb51673 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 1 Dec 2015 11:56:42 +0900 Subject: [PATCH 061/102] Bug 1178266 - Link against libatomic when necessary. r=froydnj --- build/autoconf/toolchain.m4 | 26 ++++++++++++++++++++++++++ mfbt/moz.build | 3 +++ toolkit/library/moz.build | 2 ++ 3 files changed, 31 insertions(+) diff --git a/build/autoconf/toolchain.m4 b/build/autoconf/toolchain.m4 index 120a63c350cf..90d852865a54 100644 --- a/build/autoconf/toolchain.m4 +++ b/build/autoconf/toolchain.m4 @@ -224,7 +224,33 @@ if test "$GNU_CXX"; then elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain]) fi + + AC_CACHE_CHECK([whether 64-bits std::atomic requires -latomic], + ac_cv_needs_atomic, + AC_TRY_LINK( + [#include + #include ], + [ std::atomic foo; foo = 1; ], + ac_cv_needs_atomic=no, + _SAVE_LIBS="$LIBS" + LIBS="$LIBS -latomic" + AC_TRY_LINK( + [#include + #include ], + [ std::atomic foo; foo = 1; ], + ac_cv_needs_atomic=yes, + ac_cv_needs_atomic="do not know; assuming no") + LIBS="$_SAVE_LIBS" + ) + ) + if test "$ac_cv_needs_atomic" = yes; then + MOZ_NEEDS_LIBATOMIC=1 + else + MOZ_NEEDS_LIBATOMIC= + fi + AC_SUBST(MOZ_NEEDS_LIBATOMIC) fi + if test -n "$CROSS_COMPILE"; then dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out. cat > conftest.C <' : unsafe use of type 'bool' in operation" SOURCES['/mfbt/Compression.cpp'].flags += ['-wd4804'] + +if CONFIG['MOZ_NEEDS_LIBATOMIC']: + OS_LIBS += ['atomic'] diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build index 642b58769efe..03d81c4f1a9a 100644 --- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -55,6 +55,8 @@ def Libxul(name): if not CONFIG['JS_SHARED_LIBRARY']: LIBRARY_DEFINES['STATIC_EXPORTABLE_JS_API'] = True + if CONFIG['MOZ_NEEDS_LIBATOMIC']: + OS_LIBS += ['atomic'] Libxul('xul') From 36bc760ee33225f3b659885ca2ab185d151185cf Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 26 Nov 2015 19:19:18 +0900 Subject: [PATCH 062/102] Bug 1228289 - Avoid l10n-check overwriting final package when MOZ_SIMPLE_PACKAGE_NAME is set. r=ted --- browser/locales/Makefile.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in index 13449630e7d4..c5615035ade4 100644 --- a/browser/locales/Makefile.in +++ b/browser/locales/Makefile.in @@ -228,6 +228,9 @@ l10n-check:: $(RM) -rf x-test $(NSINSTALL) -D x-test/toolkit echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc - $(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' + @# ZIP_IN='$(ZIP_IN)' will pass down the *current* value of ZIP_IN, based + @# on MOZ_SIMPLE_PACKAGE_NAME not being reset, overwriting the value it + @# would get with MOZ_SIMPLE_PACKAGE_NAME reset. + $(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' ZIP_IN='$(ZIP_IN)' MOZ_SIMPLE_PACKAGE_NAME= $(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/unpack.py $(DIST)/l10n-stage/$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH) cd $(DIST)/l10n-stage && test $$(cat $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/update.locale) = x-test From 2a82df055d0509b808347866ee743d13e30cb768 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 1 Dec 2015 17:52:26 +0900 Subject: [PATCH 063/102] Bug 1229293 - Enforce order when setting a HierarchicalStringList, and forbid reassignment. r=gps The current implementation of HierarchicalStringList allows the following: FOO.bar = [ 'foo', 'bar', ] while FOO.bar += [ 'foo', 'bar', ] would be invalid because of the StrictOrderingOnAppendList enforcement. It also allows to overwrite the entire list with a subsequent FOO.bar = [ 'baz', ] while we've explicitly forbidden such things for every other list. While in the vicinity, fix HierarchicalStringList._get_export_variable to not call the HierarchicalStringList constructor uselessly. --- intl/unicharutil/tables/moz.build | 2 +- mfbt/moz.build | 4 ++-- .../test/frontend/data/exports/moz.build | 2 -- .../mozbuild/test/frontend/test_emitter.py | 1 - python/mozbuild/mozbuild/test/test_util.py | 14 +++++++++++++- python/mozbuild/mozbuild/util.py | 16 ++++++++++++---- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/intl/unicharutil/tables/moz.build b/intl/unicharutil/tables/moz.build index 56e672850f98..ded6ee75822a 100644 --- a/intl/unicharutil/tables/moz.build +++ b/intl/unicharutil/tables/moz.build @@ -6,8 +6,8 @@ RESOURCE_FILES.entityTables = [ 'html40Latin1.properties', - 'html40Symbols.properties', 'html40Special.properties', + 'html40Symbols.properties', 'mathml20.properties', ] diff --git a/mfbt/moz.build b/mfbt/moz.build index d94dd536a3e7..d35a202ae4dc 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -67,10 +67,10 @@ EXPORTS.mozilla = [ 'Range.h', 'RangedArray.h', 'RangedPtr.h', + 'ReentrancyGuard.h', 'RefCounted.h', 'RefCountType.h', 'RefPtr.h', - 'ReentrancyGuard.h', 'ReverseIterator.h', 'RollingMean.h', 'Scoped.h', @@ -90,11 +90,11 @@ EXPORTS.mozilla = [ 'TypeTraits.h', 'UniquePtr.h', 'UniquePtrExtensions.h', + 'unused.h', 'Variant.h', 'Vector.h', 'WeakPtr.h', 'XorShift128PlusRNG.h', - 'unused.h', ] if CONFIG['OS_ARCH'] == 'WINNT': diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build index 336aecaab05d..666fbeb81009 100644 --- a/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build +++ b/python/mozbuild/mozbuild/test/frontend/data/exports/moz.build @@ -11,5 +11,3 @@ EXPORTS.mozilla.gfx += ['gfx.h'] EXPORTS.vpx = ['mem.h'] EXPORTS.vpx += ['mem2.h'] EXPORTS.nspr.private = ['pprio.h', 'pprthred.h'] -EXPORTS.overwrite = ['old.h'] -EXPORTS.overwrite = ['new.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index 00a93393e151..d14a9dff175d 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -251,7 +251,6 @@ class TestEmitterBasic(unittest.TestCase): ('mozilla/dom', ['dom1.h', 'dom2.h', 'dom3.h']), ('mozilla/gfx', ['gfx.h']), ('nspr/private', ['pprio.h', 'pprthred.h']), - ('overwrite', ['new.h']), ('vpx', ['mem.h', 'mem2.h']), ] for (expect_path, expect_headers), (actual_path, actual_headers) in \ diff --git a/python/mozbuild/mozbuild/test/test_util.py b/python/mozbuild/mozbuild/test/test_util.py index 79e83f270837..949d1c6bae80 100644 --- a/python/mozbuild/mozbuild/test/test_util.py +++ b/python/mozbuild/mozbuild/test/test_util.py @@ -269,10 +269,22 @@ class TestHierarchicalStringList(unittest.TestCase): self.EXPORTS.foo += ['bar.h'] del self.EXPORTS.foo - def test_unsorted_appends(self): + def test_unsorted(self): with self.assertRaises(UnsortedError) as ee: self.EXPORTS += ['foo.h', 'bar.h'] + with self.assertRaises(UnsortedError) as ee: + self.EXPORTS.foo = ['foo.h', 'bar.h'] + + with self.assertRaises(UnsortedError) as ee: + self.EXPORTS.foo += ['foo.h', 'bar.h'] + + def test_reassign(self): + self.EXPORTS.foo = ['foo.h'] + + with self.assertRaises(KeyError) as ee: + self.EXPORTS.foo = ['bar.h'] + def test_walk(self): l = HierarchicalStringList() l += ['root1', 'root2', 'root3'] diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py index 072d8c41d363..89e4512d5b18 100644 --- a/python/mozbuild/mozbuild/util.py +++ b/python/mozbuild/mozbuild/util.py @@ -570,13 +570,21 @@ class HierarchicalStringList(object): self._set_exportvariable(name, value) def _get_exportvariable(self, name): - return self._children.setdefault(name, HierarchicalStringList()) + child = self._children.get(name) + if not child: + child = self._children[name] = HierarchicalStringList() + return child def _set_exportvariable(self, name, value): + if name in self._children: + if value is self._get_exportvariable(name): + return + raise KeyError('global_ns', 'reassign', + '.%s' % name) + exports = self._get_exportvariable(name) - if not isinstance(value, HierarchicalStringList): - exports._check_list(value) - exports._strings = value + exports._check_list(value) + exports._strings += value def _check_list(self, value): if not isinstance(value, list): From 9e1150f3588598c57daa71a13ddfde5dca2391b4 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:23:04 +0900 Subject: [PATCH 064/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in docshell/. r=bz --- docshell/base/nsDefaultURIFixup.h | 2 +- docshell/base/timeline/RestyleTimelineMarker.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h index 5e19ee1e76c8..4a37438e9394 100644 --- a/docshell/base/nsDefaultURIFixup.h +++ b/docshell/base/nsDefaultURIFixup.h @@ -65,6 +65,6 @@ private: bool mFixupCreatedAlternateURI; nsString mKeywordProviderName; nsString mKeywordAsSent; - nsAutoCString mOriginalInput; + nsCString mOriginalInput; }; #endif diff --git a/docshell/base/timeline/RestyleTimelineMarker.h b/docshell/base/timeline/RestyleTimelineMarker.h index b5d5a02ef210..fcc37b05d854 100644 --- a/docshell/base/timeline/RestyleTimelineMarker.h +++ b/docshell/base/timeline/RestyleTimelineMarker.h @@ -34,7 +34,7 @@ public: } private: - nsAutoString mRestyleHint; + nsString mRestyleHint; }; } // namespace mozilla From 58b657359f249e99d1ef81981485a2cf4faf7ef5 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:00:39 +0900 Subject: [PATCH 065/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in dom/media/. r=cpearce --- dom/media/BufferMediaResource.h | 2 +- dom/media/MP3FrameParser.cpp | 2 +- dom/media/MP3FrameParser.h | 2 +- dom/media/MediaInfo.h | 4 ++-- dom/media/MediaResource.h | 4 ++-- dom/media/VideoUtils.cpp | 2 +- dom/media/eme/CDMProxy.h | 12 ++++++------ dom/media/gmp/GMPDecryptorChild.cpp | 16 ++++++++-------- dom/media/gmp/GMPParent.h | 2 +- dom/media/gmp/GMPServiceParent.h | 2 +- dom/media/gmp/GMPStorageParent.cpp | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dom/media/BufferMediaResource.h b/dom/media/BufferMediaResource.h index f252e6590d56..755d1da9b903 100644 --- a/dom/media/BufferMediaResource.h +++ b/dom/media/BufferMediaResource.h @@ -135,7 +135,7 @@ private: uint32_t mLength; uint32_t mOffset; nsCOMPtr mPrincipal; - const nsAutoCString mContentType; + const nsCString mContentType; }; } // namespace mozilla diff --git a/dom/media/MP3FrameParser.cpp b/dom/media/MP3FrameParser.cpp index 6448c6d344a6..242e3df00d82 100644 --- a/dom/media/MP3FrameParser.cpp +++ b/dom/media/MP3FrameParser.cpp @@ -308,7 +308,7 @@ ParseXing(const char *aBuffer) } static int64_t -FindNumVBRFrames(const nsAutoCString& aFrame) +FindNumVBRFrames(const nsCString& aFrame) { const char *buffer = aFrame.get(); const char *bufferEnd = aFrame.get() + aFrame.Length(); diff --git a/dom/media/MP3FrameParser.h b/dom/media/MP3FrameParser.h index 9941fe1aad84..d2ba791fd8ed 100644 --- a/dom/media/MP3FrameParser.h +++ b/dom/media/MP3FrameParser.h @@ -198,7 +198,7 @@ private: // If the MP3 has a variable bitrate, then there *should* be metadata about // the encoding in the first frame. We buffer the first frame here. - nsAutoCString mFirstFrame; + nsCString mFirstFrame; // While we are reading the first frame, this is the stream offset of the // last byte of that frame. -1 at all other times. diff --git a/dom/media/MediaInfo.h b/dom/media/MediaInfo.h index f7e146f3cc9b..868a5bdeb1cb 100644 --- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -75,7 +75,7 @@ public: TrackID mTrackId; - nsAutoCString mMimeType; + nsCString mMimeType; int64_t mDuration; int64_t mMediaTime; CryptoTrack mCrypto; @@ -451,7 +451,7 @@ private: uint32_t mStreamSourceID; public: - const nsAutoCString& mMimeType; + const nsCString& mMimeType; }; } // namespace mozilla diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h index 6f5c2df9edab..0fa94b3bd28d 100644 --- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -451,10 +451,10 @@ protected: // Content-Type of the channel. This is copied from the nsIChannel when the // MediaResource is created. This is constant, so accessing from any thread // is safe. - const nsAutoCString mContentType; + const nsCString mContentType; // Copy of the url of the channel resource. - nsAutoCString mContentURL; + nsCString mContentURL; // True if SetLoadInBackground() has been called with // aLoadInBackground = true, i.e. when the document load event is not diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp index 3f7d46c93c88..89e7544dd83c 100644 --- a/dom/media/VideoUtils.cpp +++ b/dom/media/VideoUtils.cpp @@ -453,7 +453,7 @@ void LogToBrowserConsole(const nsAString& aMsg) { if (!NS_IsMainThread()) { - nsAutoString msg(aMsg); + nsString msg(aMsg); nsCOMPtr task = NS_NewRunnableFunction([msg]() { LogToBrowserConsole(msg); }); NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL); diff --git a/dom/media/eme/CDMProxy.h b/dom/media/eme/CDMProxy.h index bda50daff7af..de7d3d701228 100644 --- a/dom/media/eme/CDMProxy.h +++ b/dom/media/eme/CDMProxy.h @@ -185,8 +185,8 @@ private: struct InitData { uint32_t mPromiseId; - nsAutoString mOrigin; - nsAutoString mTopLevelOrigin; + nsString mOrigin; + nsString mTopLevelOrigin; nsString mGMPName; bool mInPrivateBrowsing; }; @@ -208,7 +208,7 @@ private: dom::SessionType mSessionType; uint32_t mCreateSessionToken; PromiseId mPromiseId; - nsAutoCString mInitDataType; + nsCString mInitDataType; nsTArray mInitData; }; // GMP thread only. @@ -216,7 +216,7 @@ private: struct SessionOpData { PromiseId mPromiseId; - nsAutoCString mSessionId; + nsCString mSessionId; }; // GMP thread only. void gmp_LoadSession(nsAutoPtr aData); @@ -230,7 +230,7 @@ private: struct UpdateSessionData { PromiseId mPromiseId; - nsAutoCString mSessionId; + nsCString mSessionId; nsTArray mResponse; }; // GMP thread only. @@ -326,7 +326,7 @@ private: // destructor. only use on main thread, and always nullcheck before using! MainThreadOnlyRawPtr mKeys; - const nsAutoString mKeySystem; + const nsString mKeySystem; // Gecko Media Plugin thread. All interactions with the out-of-process // EME plugin must come from this thread. diff --git a/dom/media/gmp/GMPDecryptorChild.cpp b/dom/media/gmp/GMPDecryptorChild.cpp index fa3a57ae9a9f..0df7da8d9c53 100644 --- a/dom/media/gmp/GMPDecryptorChild.cpp +++ b/dom/media/gmp/GMPDecryptorChild.cpp @@ -79,7 +79,7 @@ GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken, uint32_t aSessionIdLength) { CALL_ON_GMP_THREAD(SendSetSessionId, - aCreateSessionToken, nsAutoCString(aSessionId, aSessionIdLength)); + aCreateSessionToken, nsCString(aSessionId, aSessionIdLength)); } void @@ -102,7 +102,7 @@ GMPDecryptorChild::RejectPromise(uint32_t aPromiseId, uint32_t aMessageLength) { CALL_ON_GMP_THREAD(SendRejectPromise, - aPromiseId, aException, nsAutoCString(aMessage, aMessageLength)); + aPromiseId, aException, nsCString(aMessage, aMessageLength)); } void @@ -115,7 +115,7 @@ GMPDecryptorChild::SessionMessage(const char* aSessionId, nsTArray msg; msg.AppendElements(aMessage, aMessageLength); CALL_ON_GMP_THREAD(SendSessionMessage, - nsAutoCString(aSessionId, aSessionIdLength), + nsCString(aSessionId, aSessionIdLength), aMessageType, Move(msg)); } @@ -125,7 +125,7 @@ GMPDecryptorChild::ExpirationChange(const char* aSessionId, GMPTimestamp aExpiryTime) { CALL_ON_GMP_THREAD(SendExpirationChange, - nsAutoCString(aSessionId, aSessionIdLength), aExpiryTime); + nsCString(aSessionId, aSessionIdLength), aExpiryTime); } void @@ -133,7 +133,7 @@ GMPDecryptorChild::SessionClosed(const char* aSessionId, uint32_t aSessionIdLength) { CALL_ON_GMP_THREAD(SendSessionClosed, - nsAutoCString(aSessionId, aSessionIdLength)); + nsCString(aSessionId, aSessionIdLength)); } void @@ -145,9 +145,9 @@ GMPDecryptorChild::SessionError(const char* aSessionId, uint32_t aMessageLength) { CALL_ON_GMP_THREAD(SendSessionError, - nsAutoCString(aSessionId, aSessionIdLength), + nsCString(aSessionId, aSessionIdLength), aException, aSystemCode, - nsAutoCString(aMessage, aMessageLength)); + nsCString(aMessage, aMessageLength)); } void @@ -160,7 +160,7 @@ GMPDecryptorChild::KeyStatusChanged(const char* aSessionId, nsAutoTArray kid; kid.AppendElements(aKeyId, aKeyIdLength); CALL_ON_GMP_THREAD(SendKeyStatusChanged, - nsAutoCString(aSessionId, aSessionIdLength), kid, + nsCString(aSessionId, aSessionIdLength), kid, aStatus); } diff --git a/dom/media/gmp/GMPParent.h b/dom/media/gmp/GMPParent.h index c42437775413..c0d7323eec25 100644 --- a/dom/media/gmp/GMPParent.h +++ b/dom/media/gmp/GMPParent.h @@ -209,7 +209,7 @@ private: nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. // NodeId the plugin is assigned to, or empty if the the plugin is not // assigned to a NodeId. - nsAutoCString mNodeId; + nsCString mNodeId; // This is used for GMP content in the parent, there may be more of these in // the content processes. RefPtr mGMPContentParent; diff --git a/dom/media/gmp/GMPServiceParent.h b/dom/media/gmp/GMPServiceParent.h index 929aff88a500..5d8dc3ae0ca7 100644 --- a/dom/media/gmp/GMPServiceParent.h +++ b/dom/media/gmp/GMPServiceParent.h @@ -156,7 +156,7 @@ private: void Update(const nsCString& aPlugin, const nsCString& aInstance, char aId, const nsCString& aState); private: - struct State { nsAutoCString mStateSequence; nsCString mLastStateDescription; }; + struct State { nsCString mStateSequence; nsCString mLastStateDescription; }; typedef nsClassHashtable StatesByInstance; typedef nsClassHashtable StateInstancesByPlugin; StateInstancesByPlugin mStates; diff --git a/dom/media/gmp/GMPStorageParent.cpp b/dom/media/gmp/GMPStorageParent.cpp index 46ad47fde7d3..58ac58953e9d 100644 --- a/dom/media/gmp/GMPStorageParent.cpp +++ b/dom/media/gmp/GMPStorageParent.cpp @@ -482,7 +482,7 @@ private: // Hash record name to record data. nsClassHashtable mRecords; - const nsAutoCString mNodeId; + const nsCString mNodeId; const nsString mGMPName; }; From ce9a5909d195e4db0c01b89f295bdc7589f65638 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:26:54 +0900 Subject: [PATCH 066/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in gfx/. r=jrmuizel --- gfx/thebes/gfxSVGGlyphs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/thebes/gfxSVGGlyphs.h b/gfx/thebes/gfxSVGGlyphs.h index 130ac86b47e6..d07649ad7fd2 100644 --- a/gfx/thebes/gfxSVGGlyphs.h +++ b/gfx/thebes/gfxSVGGlyphs.h @@ -68,7 +68,7 @@ private: nsBaseHashtable mGlyphIdMap; - nsAutoCString mSVGGlyphsDocumentURI; + nsCString mSVGGlyphsDocumentURI; }; /** From 88221586ddda1ea8fc5ca124b7967222290d9183 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:06:57 +0900 Subject: [PATCH 067/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in js/xpconnect/. r=mccr8 --- js/xpconnect/src/XPCJSRuntime.cpp | 6 +++--- js/xpconnect/src/xpcpublic.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index f880c7317c1a..c5e0f9e54446 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1911,7 +1911,7 @@ ReportZoneStats(const JS::ZoneStats& zStats, bool anonymize, size_t* gcTotalOut = nullptr) { - const nsAutoCString& pathPrefix = extras.pathPrefix; + const nsCString& pathPrefix = extras.pathPrefix; size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0; MOZ_ASSERT(!gcTotalOut == zStats.isTotals); @@ -2223,8 +2223,8 @@ ReportCompartmentStats(const JS::CompartmentStats& cStats, static const nsDependentCString addonPrefix("explicit/add-ons/"); size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0; - nsAutoCString cJSPathPrefix = extras.jsPathPrefix; - nsAutoCString cDOMPathPrefix = extras.domPathPrefix; + nsAutoCString cJSPathPrefix(extras.jsPathPrefix); + nsAutoCString cDOMPathPrefix(extras.domPathPrefix); nsresult rv; MOZ_ASSERT(!gcTotalOut == cStats.isTotals); diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index d8101d6d3698..9d2c644e86ef 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -371,7 +371,7 @@ public: ZoneStatsExtras() {} - nsAutoCString pathPrefix; + nsCString pathPrefix; private: ZoneStatsExtras(const ZoneStatsExtras& other) = delete; @@ -386,8 +386,8 @@ public: : sizeOfXPCPrivate(0) {} - nsAutoCString jsPathPrefix; - nsAutoCString domPathPrefix; + nsCString jsPathPrefix; + nsCString domPathPrefix; nsCOMPtr location; size_t sizeOfXPCPrivate; From b2e31e6ade33e97d7e492277affc73c7d93b924f Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:30:23 +0900 Subject: [PATCH 068/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in netwerk/. r=mcmanus --- netwerk/base/BackgroundFileSaver.h | 2 +- netwerk/protocol/about/nsAboutCacheEntry.h | 2 +- netwerk/protocol/http/HttpBaseChannel.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/netwerk/base/BackgroundFileSaver.h b/netwerk/base/BackgroundFileSaver.h index 6caedd3630a1..587be83fceb9 100644 --- a/netwerk/base/BackgroundFileSaver.h +++ b/netwerk/base/BackgroundFileSaver.h @@ -205,7 +205,7 @@ private: * The SHA 256 hash in raw bytes of the downloaded file. This is written * by the worker thread but can be read on the main thread. */ - nsAutoCString mSha256; + nsCString mSha256; /** * Whether or not to compute the hash. Must be set on the main thread before diff --git a/netwerk/protocol/about/nsAboutCacheEntry.h b/netwerk/protocol/about/nsAboutCacheEntry.h index 6ff77c32e907..2c15df2a749e 100644 --- a/netwerk/protocol/about/nsAboutCacheEntry.h +++ b/netwerk/protocol/about/nsAboutCacheEntry.h @@ -60,7 +60,7 @@ private: uint32_t *aWriteCount); private: - nsAutoCString mStorageName, mEnhanceId; + nsCString mStorageName, mEnhanceId; nsCOMPtr mLoadInfo; nsCOMPtr mCacheURI; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 82075c343346..637fcb2437ab 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1885,8 +1885,10 @@ class CookieNotifierRunnable : public nsRunnable { public: CookieNotifierRunnable(HttpBaseChannel* aChannel, char const * aCookie) - : mChannel(aChannel), mCookie(aCookie) - { } + : mChannel(aChannel) + { + CopyASCIItoUTF16(aCookie, mCookie); + } NS_IMETHOD Run() { @@ -1901,7 +1903,7 @@ public: private: RefPtr mChannel; - NS_ConvertASCIItoUTF16 mCookie; + nsString mCookie; }; } // namespace From 9403734fe8110965fb47a044c3f1d63b70a3a8d0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:31:38 +0900 Subject: [PATCH 069/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in toolkit/. r=froydnj --- toolkit/components/downloads/nsDownloadManager.h | 2 +- toolkit/components/filepicker/nsFileView.cpp | 2 +- toolkit/components/telemetry/ThreadHangStats.h | 2 +- toolkit/components/url-classifier/nsCheckSummedOutputStream.h | 2 +- toolkit/xre/ProfileReset.h | 2 +- toolkit/xre/nsUpdateDriver.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/toolkit/components/downloads/nsDownloadManager.h b/toolkit/components/downloads/nsDownloadManager.h index aaf09bc49920..488e4840d1dd 100644 --- a/toolkit/components/downloads/nsDownloadManager.h +++ b/toolkit/components/downloads/nsDownloadManager.h @@ -433,7 +433,7 @@ private: /** * Stores the SHA-256 hash associated with the downloaded file. */ - nsAutoCString mHash; + nsCString mHash; /** * Stores the certificate chains in an nsIArray of nsIX509CertList of diff --git a/toolkit/components/filepicker/nsFileView.cpp b/toolkit/components/filepicker/nsFileView.cpp index 8aef9e7ca166..383e7b8fce7c 100644 --- a/toolkit/components/filepicker/nsFileView.cpp +++ b/toolkit/components/filepicker/nsFileView.cpp @@ -45,7 +45,7 @@ public: NS_DECL_NSIAUTOCOMPLETERESULT nsTArray mValues; - nsAutoString mSearchString; + nsString mSearchString; uint16_t mSearchResult; private: ~nsFileResult() {} diff --git a/toolkit/components/telemetry/ThreadHangStats.h b/toolkit/components/telemetry/ThreadHangStats.h index eaf43edc8d5c..2513c347b4ad 100644 --- a/toolkit/components/telemetry/ThreadHangStats.h +++ b/toolkit/components/telemetry/ThreadHangStats.h @@ -171,7 +171,7 @@ public: class ThreadHangStats { private: - nsAutoCString mName; + nsCString mName; public: TimeHistogram mActivity; diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h index 7e049aa2502e..c2fe26b5f565 100644 --- a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h +++ b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h @@ -33,7 +33,7 @@ protected: virtual ~nsCheckSummedOutputStream() { nsSafeFileOutputStream::Close(); } nsCOMPtr mHash; - nsAutoCString mCheckSum; + nsCString mCheckSum; }; // returns a file output stream which can be QI'ed to nsIFileOutputStream. diff --git a/toolkit/xre/ProfileReset.h b/toolkit/xre/ProfileReset.h index 1eada066677a..de6d40c246c2 100644 --- a/toolkit/xre/ProfileReset.h +++ b/toolkit/xre/ProfileReset.h @@ -77,5 +77,5 @@ private: nsCOMPtr mProfileDir; nsCOMPtr mProfileLocalDir; nsCOMPtr mTargetDir; - nsAutoString mLeafName; + nsString mLeafName; }; diff --git a/toolkit/xre/nsUpdateDriver.h b/toolkit/xre/nsUpdateDriver.h index 104b0520d247..0d0852ec3744 100644 --- a/toolkit/xre/nsUpdateDriver.h +++ b/toolkit/xre/nsUpdateDriver.h @@ -89,7 +89,7 @@ private: nsCOMPtr mOSApplyToDir; int mArgc; char **mArgv; - nsAutoCString mAppVersion; + nsCString mAppVersion; bool mIsOSUpdate; }; From 57327a0e53f68a595702260b0b5773a181ccd65e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:30:38 +0900 Subject: [PATCH 070/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in rdf/. r=Pike --- rdf/base/nsRDFService.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rdf/base/nsRDFService.h b/rdf/base/nsRDFService.h index 05cba906d2f1..0cc8c79ce4f4 100644 --- a/rdf/base/nsRDFService.h +++ b/rdf/base/nsRDFService.h @@ -44,7 +44,7 @@ protected: PLDHashTable mDates; PLDHashTable mBlobs; - nsAutoCString mLastURIPrefix; + nsCString mLastURIPrefix; nsCOMPtr mLastFactory; nsCOMPtr mDefaultResourceFactory; From 4005d567f991a81839715e8cde0b1e4b4f3720d2 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:30:46 +0900 Subject: [PATCH 071/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in security/manager/. r=keeler --- security/manager/ssl/nsSecurityHeaderParser.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/manager/ssl/nsSecurityHeaderParser.h b/security/manager/ssl/nsSecurityHeaderParser.h index 0237895cfa05..eee29127e680 100644 --- a/security/manager/ssl/nsSecurityHeaderParser.h +++ b/security/manager/ssl/nsSecurityHeaderParser.h @@ -12,8 +12,8 @@ // Utility class for handing back parsed directives and (optional) values class nsSecurityHeaderDirective : public mozilla::LinkedListElement { public: - nsAutoCString mName; - nsAutoCString mValue; + nsCString mName; + nsCString mValue; }; // This class parses security-related HTTP headers like @@ -67,7 +67,7 @@ private: const char *mCursor; nsSecurityHeaderDirective *mDirective; - nsAutoCString mOutput; + nsCString mOutput; bool mError; }; From 4c1c46f7d35919b194d24221a9d6b281301e63b0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:31:56 +0900 Subject: [PATCH 072/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in widget/. r=roc --- widget/GfxInfoBase.cpp | 2 +- widget/TextEventDispatcher.h | 2 +- widget/gtk/WakeLockListener.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index 438e6cceaa1c..1121ba9b1241 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -1136,7 +1136,7 @@ nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle aResu const nsCString& GfxInfoBase::GetApplicationVersion() { - static nsAutoCString version; + static nsCString version; static bool versionInitialized = false; if (!versionInitialized) { // If we fail to get the version, we will not try again. diff --git a/widget/TextEventDispatcher.h b/widget/TextEventDispatcher.h index 018168e2cbd4..d95f1284547e 100644 --- a/widget/TextEventDispatcher.h +++ b/widget/TextEventDispatcher.h @@ -269,7 +269,7 @@ private: void Clear(); private: - nsAutoString mString; + nsString mString; RefPtr mClauses; TextRange mCaret; diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp index ec16012c8065..d321d00c4848 100644 --- a/widget/gtk/WakeLockListener.cpp +++ b/widget/gtk/WakeLockListener.cpp @@ -63,7 +63,7 @@ private: void InhibitFailed(); void InhibitSucceeded(uint32_t aInhibitRequest); - nsAutoCString mTopic; + nsCString mTopic; DBusConnection* mConnection; DesktopEnvironment mDesktopEnvironment; From 7094b49a7af372ea8588b482add362fec773b3de Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 19 Nov 2015 18:32:05 +0900 Subject: [PATCH 073/102] Bug 1225682 - Don't use nsAuto{,C}String as class member variables in xpfe/. r=neil --- xpfe/appshell/nsXULWindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpfe/appshell/nsXULWindow.h b/xpfe/appshell/nsXULWindow.h index 4cacfd051afb..882e6147e4f3 100644 --- a/xpfe/appshell/nsXULWindow.h +++ b/xpfe/appshell/nsXULWindow.h @@ -174,7 +174,7 @@ public: ~nsContentShellInfo(); public: - nsAutoString id; // The identifier of the content shell + nsString id; // The identifier of the content shell nsWeakPtr child; // content shell (weak reference to nsIDocShellTreeItem) }; From 373cfa136570fb3db59a67f4fa3b78320b445dc2 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Wed, 2 Dec 2015 15:37:09 +1300 Subject: [PATCH 074/102] Bug 1229508 - Support current and previous GMP_API_DECRYPTORs. r=gerald To allow GMPs time to update to new GMPDecryptor versions, we support the latest GMPDecryptor version, and the previous. In order to support a consistent interface to Gecko, we adapt the previous GMPDecryptor version to the latest in the GMP child process. So Gecko always thinks it's talking to the latest version. We also make gmp-fake deliberately support the previous GMPDecryptor version, to ensure this code path remains tested. --- dom/media/gmp-plugin/fake.info | 2 +- dom/media/gmp-plugin/gmp-fake.cpp | 2 +- dom/media/gmp/GMPContentChild.cpp | 11 ++++++++++- dom/media/gmp/GMPParent.cpp | 6 ++++++ dom/media/gmp/gmp-api/gmp-decryption.h | 4 +++- media/gmp-clearkey/0.1/clearkey.info.in | 4 ++-- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/dom/media/gmp-plugin/fake.info b/dom/media/gmp-plugin/fake.info index 59813a220ca5..b0592ff639cd 100644 --- a/dom/media/gmp-plugin/fake.info +++ b/dom/media/gmp-plugin/fake.info @@ -1,5 +1,5 @@ Name: fake -Description: Fake GMP Plugin +Description: Fake GMP Plugin, which deliberately uses GMP_API_DECRYPTOR_BACKWARDS_COMPAT for its decryptor. Version: 1.0 APIs: decode-video[h264:broken], eme-decrypt-v7[fake] Libraries: dxva2.dll diff --git a/dom/media/gmp-plugin/gmp-fake.cpp b/dom/media/gmp-plugin/gmp-fake.cpp index 48dd8c43918a..53946276adf7 100644 --- a/dom/media/gmp-plugin/gmp-fake.cpp +++ b/dom/media/gmp-plugin/gmp-fake.cpp @@ -72,7 +72,7 @@ extern "C" { // happens when decoder init fails. return GMPGenericErr; #if defined(GMP_FAKE_SUPPORT_DECRYPT) - } else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) { + } else if (!strcmp (aApiName, GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) { *aPluginApi = new FakeDecryptor(static_cast (aHostAPI)); return GMPNoErr; } else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) { diff --git a/dom/media/gmp/GMPContentChild.cpp b/dom/media/gmp/GMPContentChild.cpp index 8cb03e4a1272..9c35b03874bc 100644 --- a/dom/media/gmp/GMPContentChild.cpp +++ b/dom/media/gmp/GMPContentChild.cpp @@ -119,7 +119,16 @@ GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) void* session = nullptr; GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session); if (err != GMPNoErr || !session) { - return false; + // We Adapt the previous GMPDecryptor version to the current, so that + // Gecko thinks it's only talking to the current version. Helpfully, + // v7 is ABI compatible with v8, it only has different enumerations. + // If the GMP uses a v8-only enum value in an IPDL message, the IPC + // layer will terminate, so we rev'd the API version to signal to the + // GMP that it's safe to use the new enum values. + err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_BACKWARDS_COMPAT, host, &session); + if (err != GMPNoErr || !session) { + return false; + } } child->Init(static_cast(session)); diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index df39aa6efd90..7154262ef4aa 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -868,6 +868,12 @@ GMPParent::ReadGMPMetaData() } } + // We support the current GMPDecryptor version, and the previous. + // We Adapt the previous to the current in the GMPContentChild. + if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) { + cap->mAPIName.AssignLiteral(GMP_API_DECRYPTOR); + } + if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) { mCanDecrypt = true; diff --git a/dom/media/gmp/gmp-api/gmp-decryption.h b/dom/media/gmp/gmp-api/gmp-decryption.h index 86c845a424a8..ddf27cfb041b 100644 --- a/dom/media/gmp/gmp-api/gmp-decryption.h +++ b/dom/media/gmp/gmp-api/gmp-decryption.h @@ -244,7 +244,9 @@ enum GMPSessionType { kGMPSessionInvalid = 2 // Must always be last. }; -#define GMP_API_DECRYPTOR "eme-decrypt-v7" +// Gecko supports the current GMPDecryptor version, and the previous. +#define GMP_API_DECRYPTOR "eme-decrypt-v8" +#define GMP_API_DECRYPTOR_BACKWARDS_COMPAT "eme-decrypt-v7" // API exposed by plugin library to manage decryption sessions. // When the Host requests this by calling GMPGetAPIFunc(). diff --git a/media/gmp-clearkey/0.1/clearkey.info.in b/media/gmp-clearkey/0.1/clearkey.info.in index aca4bc286021..392f47f20add 100644 --- a/media/gmp-clearkey/0.1/clearkey.info.in +++ b/media/gmp-clearkey/0.1/clearkey.info.in @@ -2,9 +2,9 @@ Name: clearkey Description: ClearKey Gecko Media Plugin Version: 1 #ifdef ENABLE_WMF -APIs: eme-decrypt-v7[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey] +APIs: eme-decrypt-v8[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey] Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll #else -APIs: eme-decrypt-v7[org.w3.clearkey] +APIs: eme-decrypt-v8[org.w3.clearkey] Libraries: #endif From 0f7fe3417c1358d509a1f81ee82ad49cd3e3219b Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Dec 2015 22:21:42 -0500 Subject: [PATCH 075/102] Bug 1156392 - Remove an ifdef condition that we don't need any more. r=tn --- layout/generic/nsGfxScrollFrame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index c186ae0d35cb..6323743cb220 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1096,7 +1096,7 @@ ScrollFrameHelper::HandleScrollbarStyleSwitching() } } -#if defined(MOZ_B2G) || (defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)) +#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID) static bool IsFocused(nsIContent* aContent) { // Some content elements, like the GetContent() of a scroll frame @@ -1138,7 +1138,7 @@ ScrollFrameHelper::WantAsyncScroll() const bool isHScrollable = !!(directions & nsIScrollableFrame::HORIZONTAL) && (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN); -#if defined(MOZ_B2G) || (defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)) +#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID) // Mobile platforms need focus to scroll. bool canScrollWithoutScrollbars = IsFocused(mOuter->GetContent()); #else From 1b1d719d0fa6db8d6c385eba3f49fcd2705277b7 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 2 Dec 2015 13:20:00 +0900 Subject: [PATCH 076/102] Bug 1213589 part.1 Make ContentEventHandler::GetTextLength() and GetNativeTextLength() called only with a text node r=smaug --- dom/events/ContentEventHandler.cpp | 69 ++++++++++++++++++------------ dom/events/ContentEventHandler.h | 20 ++++++--- dom/events/IMEContentObserver.cpp | 24 ++--------- 3 files changed, 58 insertions(+), 55 deletions(-) diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 18b3629f7950..a4c2c584dbe2 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -327,6 +327,9 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent, { MOZ_ASSERT(aEndOffset >= aStartOffset, "aEndOffset must be equals or larger than aStartOffset"); + if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) { + return 0; + } if (aStartOffset == aEndOffset) { return 0; } @@ -338,11 +341,23 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent, ContentEventHandler::GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength) { + if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) { + return 0; + } return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength); } -static inline uint32_t -GetBRLength(LineBreakType aLineBreakType) +/* static */ uint32_t +ContentEventHandler::GetNativeTextLengthBefore(nsIContent* aContent) +{ + if (NS_WARN_IF(aContent->IsNodeOfType(nsINode::eTEXT))) { + return 0; + } + return IsContentBR(aContent) ? GetBRLength(LINE_BREAK_TYPE_NATIVE) : 0; +} + +/* static inline */ uint32_t +ContentEventHandler::GetBRLength(LineBreakType aLineBreakType) { #if defined(XP_WIN) // Length of \r\n @@ -357,29 +372,26 @@ ContentEventHandler::GetTextLength(nsIContent* aContent, LineBreakType aLineBreakType, uint32_t aMaxLength) { - if (aContent->IsNodeOfType(nsINode::eTEXT)) { - uint32_t textLengthDifference = + MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT)); + + uint32_t textLengthDifference = #if defined(XP_WIN) - // On Windows, the length of a native newline ("\r\n") is twice the length - // of the XP newline ("\n"), so XP length is equal to the length of the - // native offset plus the number of newlines encountered in the string. - (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? - CountNewlinesInXPLength(aContent, aMaxLength) : 0; + // On Windows, the length of a native newline ("\r\n") is twice the length + // of the XP newline ("\n"), so XP length is equal to the length of the + // native offset plus the number of newlines encountered in the string. + (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? + CountNewlinesInXPLength(aContent, aMaxLength) : 0; #else - // On other platforms, the native and XP newlines are the same. - 0; + // On other platforms, the native and XP newlines are the same. + 0; #endif - const nsTextFragment* text = aContent->GetText(); - if (!text) { - return 0; - } - uint32_t length = std::min(text->GetLength(), aMaxLength); - return length + textLengthDifference; - } else if (IsContentBR(aContent)) { - return GetBRLength(aLineBreakType); + const nsTextFragment* text = aContent->GetText(); + if (!text) { + return 0; } - return 0; + uint32_t length = std::min(text->GetLength(), aMaxLength); + return length + textLengthDifference; } static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset) @@ -460,6 +472,8 @@ ContentEventHandler::GetTextLengthInRange(nsIContent* aContent, uint32_t aXPEndOffset, LineBreakType aLineBreakType) { + MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT)); + return aLineBreakType == LINE_BREAK_TYPE_NATIVE ? GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) : aXPEndOffset - aXPStartOffset; @@ -473,6 +487,8 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges, int32_t aXPEndOffset, LineBreakType aLineBreakType) { + MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT)); + nsIFrame* frame = aContent->GetPrimaryFrame(); if (!frame) { // It is a non-rendered content, create an empty range for it. @@ -689,7 +705,10 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, } nsIContent* content = static_cast(node); - uint32_t textLength = GetTextLength(content, aLineBreakType); + uint32_t textLength = + content->IsNodeOfType(nsINode::eTEXT) ? + GetTextLength(content, aLineBreakType) : + (IsContentBR(content) ? GetBRLength(aLineBreakType) : 0); if (!textLength) { continue; } @@ -1443,13 +1462,7 @@ ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, *aOffset += GetTextLength(content, aLineBreakType); } } else if (IsContentBR(content)) { -#if defined(XP_WIN) - // On Windows, the length of the newline is 2. - *aOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; -#else - // On other platforms, the length of the newline is 1. - *aOffset += 1; -#endif + *aOffset += GetBRLength(aLineBreakType); } } return NS_OK; diff --git a/dom/events/ContentEventHandler.h b/dom/events/ContentEventHandler.h index 6883ea14af0e..ffd73437a860 100644 --- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -92,24 +92,30 @@ public: uint32_t* aOffset, LineBreakType aLineBreakType); // Computes the native text length between aStartOffset and aEndOffset of - // aContent. Currently, this method supports only text node or br element - // for aContent. + // aContent. aContent must be a text node. static uint32_t GetNativeTextLength(nsIContent* aContent, uint32_t aStartOffset, uint32_t aEndOffset); - // Get the native text length of a content node excluding any children + // Get the native text length of aContent. aContent must be a text node. static uint32_t GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength = UINT32_MAX); + // Get the native text length which is inserted before aContent. + // aContent should be a
element for now. + static uint32_t GetNativeTextLengthBefore(nsIContent* aContent); + +protected: + // Get the text length of aContent. aContent must be a text node. + static uint32_t GetTextLength(nsIContent* aContent, + LineBreakType aLineBreakType, + uint32_t aMaxLength = UINT32_MAX); // Get the text length of a given range of a content node in // the given line break type. static uint32_t GetTextLengthInRange(nsIContent* aContent, uint32_t aXPStartOffset, uint32_t aXPEndOffset, LineBreakType aLineBreakType); -protected: - static uint32_t GetTextLength(nsIContent* aContent, - LineBreakType aLineBreakType, - uint32_t aMaxLength = UINT32_MAX); + // Get the line breaker length. + static inline uint32_t GetBRLength(LineBreakType aLineBreakType); static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent); static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent); static LineBreakType GetLineBreakType(bool aUseNativeLineBreak); diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index 9fa14f638782..edbe236b12cd 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -1026,16 +1026,6 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument, MaybeNotifyIMEOfTextChange(data); } -static nsIContent* -GetContentBR(dom::Element* aElement) -{ - if (!aElement->IsNodeOfType(nsINode::eCONTENT)) { - return nullptr; - } - nsIContent* content = static_cast(aElement); - return content->IsHTMLElement(nsGkAtoms::br) ? content : nullptr; -} - void IMEContentObserver::AttributeWillChange(nsIDocument* aDocument, dom::Element* aElement, @@ -1044,9 +1034,8 @@ IMEContentObserver::AttributeWillChange(nsIDocument* aDocument, int32_t aModType, const nsAttrValue* aNewValue) { - nsIContent *content = GetContentBR(aElement); - mPreAttrChangeLength = content ? - ContentEventHandler::GetNativeTextLength(content) : 0; + mPreAttrChangeLength = + ContentEventHandler::GetNativeTextLengthBefore(aElement); } void @@ -1066,19 +1055,14 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument, return; } - nsIContent *content = GetContentBR(aElement); - if (!content) { - return; - } - uint32_t postAttrChangeLength = - ContentEventHandler::GetNativeTextLength(content); + ContentEventHandler::GetNativeTextLengthBefore(aElement); if (postAttrChangeLength == mPreAttrChangeLength) { return; } uint32_t start; nsresult rv = - ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content, + ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aElement, 0, &start, LINE_BREAK_TYPE_NATIVE); NS_ENSURE_SUCCESS_VOID(rv); From 18395c1495da63aacd1ec233cdb8872c9caa09f0 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 2 Dec 2015 13:20:00 +0900 Subject: [PATCH 077/102] Bug 1213589 part.2 Clean up GenerateFlatTextContent(), GelerateFlatFontRanges() and GetFlatTextOffsetOfRange() of ContentEventHandler r=smaug --- dom/events/ContentEventHandler.cpp | 221 +++++++++++++++++------------ dom/events/ContentEventHandler.h | 73 ++++++++-- dom/events/IMEContentObserver.cpp | 76 ++++++---- dom/events/IMEContentObserver.h | 1 + 4 files changed, 242 insertions(+), 129 deletions(-) diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index a4c2c584dbe2..b6e54a7fe05b 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -407,37 +407,41 @@ static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset) #endif } -static nsresult GenerateFlatTextContent(nsRange* aRange, - nsAFlatString& aString, - LineBreakType aLineBreakType) +nsresult +ContentEventHandler::GenerateFlatTextContent(nsRange* aRange, + nsAFlatString& aString, + LineBreakType aLineBreakType) { - nsCOMPtr iter = NS_NewContentIterator(); - iter->Init(aRange); - NS_ASSERTION(aString.IsEmpty(), "aString must be empty string"); nsINode* startNode = aRange->GetStartParent(); - NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); nsINode* endNode = aRange->GetEndParent(); - NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); + if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { + return NS_ERROR_FAILURE; + } if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) { - nsIContent* content = static_cast(startNode); + nsIContent* content = startNode->AsContent(); AppendSubString(aString, content, aRange->StartOffset(), aRange->EndOffset() - aRange->StartOffset()); ConvertToNativeNewlines(aString); return NS_OK; } + nsCOMPtr iter = NS_NewContentIterator(); + nsresult rv = iter->Init(aRange); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); - if (!node) { + if (NS_WARN_IF(!node)) { break; } - if (!node->IsNodeOfType(nsINode::eCONTENT)) { + if (!node->IsContent()) { continue; } - nsIContent* content = static_cast(node); + nsIContent* content = node->AsContent(); if (content->IsNodeOfType(nsINode::eTEXT)) { if (content == startNode) { @@ -578,14 +582,18 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange, nsINode* startNode = aRange->GetStartParent(); nsINode* endNode = aRange->GetEndParent(); - if (NS_WARN_IF(!startNode || !endNode)) { + if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { return NS_ERROR_FAILURE; } // baseOffset is the flattened offset of each content node. int32_t baseOffset = 0; nsCOMPtr iter = NS_NewContentIterator(); - for (iter->Init(aRange); !iter->IsDone(); iter->Next()) { + nsresult rv = iter->Init(aRange); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); if (NS_WARN_IF(!node)) { break; @@ -690,20 +698,22 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, nsCOMPtr iter = NS_NewPreContentIterator(); nsresult rv = iter->Init(mRootContent); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } uint32_t offset = 0; uint32_t endOffset = aOffset + aLength; bool startSet = false; for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); - if (!node) { + if (NS_WARN_IF(!node)) { break; } - if (!node->IsNodeOfType(nsINode::eCONTENT)) { + if (!node->IsContent()) { continue; } - nsIContent* content = static_cast(node); + nsIContent* content = node->AsContent(); uint32_t textLength = content->IsNodeOfType(nsINode::eTEXT) ? @@ -722,25 +732,31 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { xpOffset = ConvertToXPOffset(content, xpOffset); } - } - if (aExpandToClusterBoundaries) { - uint32_t oldXPOffset = xpOffset; - rv = ExpandToClusterBoundary(content, false, &xpOffset); - NS_ENSURE_SUCCESS(rv, rv); - if (aNewOffset) { - // This is correct since a cluster shouldn't include line break. - *aNewOffset -= (oldXPOffset - xpOffset); + if (aExpandToClusterBoundaries) { + uint32_t oldXPOffset = xpOffset; + rv = ExpandToClusterBoundary(content, false, &xpOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (aNewOffset) { + // This is correct since a cluster shouldn't include line break. + *aNewOffset -= (oldXPOffset - xpOffset); + } } } rv = aRange->SetStart(content, int32_t(xpOffset)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } startSet = true; if (aLength == 0) { // Ensure that the end offset and the start offset are same. rv = aRange->SetEnd(content, int32_t(xpOffset)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return NS_OK; } } @@ -754,7 +770,9 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, } if (aExpandToClusterBoundaries) { rv = ExpandToClusterBoundary(content, true, &xpOffset); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } } else { // Use first position of next node, because the end node is ignored @@ -768,7 +786,9 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, } rv = aRange->SetEnd(endNode, int32_t(xpOffset)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return NS_OK; } @@ -782,14 +802,18 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, if (!startSet) { MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); rv = aRange->SetStart(mRootContent, int32_t(mRootContent->GetChildCount())); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } if (aNewOffset) { *aNewOffset = offset; } } rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount())); - NS_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetEnd failed"); - return rv; + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; } /* static */ LineBreakType @@ -868,8 +892,8 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent) "The reply string must be empty"); LineBreakType lineBreakType = GetLineBreakType(aEvent); - rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, - &aEvent->mReply.mOffset, lineBreakType); + rv = GetFlatTextLengthBefore(mFirstSelectedRange, + &aEvent->mReply.mOffset, lineBreakType); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr anchorNode, focusNode; @@ -1140,8 +1164,8 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent) nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect); if (caretFrame) { uint32_t offset; - rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset, - lineBreakType); + rv = GetFlatTextLengthBefore(mFirstSelectedRange, + &offset, lineBreakType); NS_ENSURE_SUCCESS(rv, rv); if (offset == aEvent->mInput.mOffset) { rv = ConvertToRootRelativeOffset(caretFrame, caretRect); @@ -1320,8 +1344,9 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) return NS_OK; } - rv = GetFlatTextOffsetOfRange(mRootContent, tentativeCaretOffsets.content, - tentativeCaretOffsets.offset, + rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0), + NodePosition(tentativeCaretOffsets), + mRootContent, &aEvent->mReply.mTentativeCaretOffset, GetLineBreakType(aEvent)); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -1344,10 +1369,13 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) textframe->GetCharacterOffsetAtFramePoint(ptInTarget); NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE); uint32_t offset; - rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content, - contentOffsets.offset, &offset, + rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0), + NodePosition(contentOffsets), + mRootContent, &offset, GetLineBreakType(aEvent)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget); textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak); @@ -1409,76 +1437,89 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent) } /* static */ nsresult -ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsINode* aNode, - int32_t aNodeOffset, - uint32_t* aOffset, - LineBreakType aLineBreakType) +ContentEventHandler::GetFlatTextLengthInRange( + const NodePosition& aStartPosition, + const NodePosition& aEndPosition, + nsIContent* aRootContent, + uint32_t* aLength, + LineBreakType aLineBreakType) { - NS_ENSURE_STATE(aRootContent); - NS_ASSERTION(aOffset, "param is invalid"); - - RefPtr prev = new nsRange(aRootContent); - nsCOMPtr rootDOMNode(do_QueryInterface(aRootContent)); - prev->SetStart(rootDOMNode, 0); - - nsCOMPtr startDOMNode(do_QueryInterface(aNode)); - NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode"); - - nsCOMPtr iter = NS_NewContentIterator(); - - if (aNode->Length() >= static_cast(aNodeOffset)) { - // Offset is within node's length; set end of range to that offset - prev->SetEnd(startDOMNode, aNodeOffset); - iter->Init(prev); - } else if (aNode != static_cast(aRootContent)) { - // Offset is past node's length; set end of range to end of node - prev->SetEndAfter(startDOMNode); - iter->Init(prev); - } else { - // Offset is past the root node; set end of range to end of root node - iter->Init(aRootContent); + if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) || + NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) { + return NS_ERROR_INVALID_ARG; } - nsCOMPtr startNode = do_QueryInterface(startDOMNode); - nsINode* endNode = aNode; + RefPtr prev = new nsRange(aRootContent); + nsresult rv = aStartPosition.SetToRangeStart(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - *aOffset = 0; + nsCOMPtr iter = NS_NewContentIterator(); + if (aEndPosition.OffsetIsValid()) { + // Offset is within node's length; set end of range to that offset + rv = aEndPosition.SetToRangeEnd(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = iter->Init(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else if (aEndPosition.mNode != aRootContent) { + // Offset is past node's length; set end of range to end of node + rv = aEndPosition.SetToRangeEndAfter(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = iter->Init(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + // Offset is past the root node; set end of range to end of root node + rv = iter->Init(aRootContent); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + *aLength = 0; for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); - if (!node) { + if (NS_WARN_IF(!node)) { break; } - if (!node->IsNodeOfType(nsINode::eCONTENT)) { + if (!node->IsContent()) { continue; } - nsIContent* content = static_cast(node); + nsIContent* content = node->AsContent(); if (node->IsNodeOfType(nsINode::eTEXT)) { // Note: our range always starts from offset 0 - if (node == endNode) { - *aOffset += GetTextLength(content, aLineBreakType, aNodeOffset); + if (node == aEndPosition.mNode) { + *aLength += GetTextLength(content, aLineBreakType, + aEndPosition.mOffset); } else { - *aOffset += GetTextLength(content, aLineBreakType); + *aLength += GetTextLength(content, aLineBreakType); } } else if (IsContentBR(content)) { - *aOffset += GetBRLength(aLineBreakType); + *aLength += GetBRLength(aLineBreakType); } } return NS_OK; } -/* static */ nsresult -ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsRange* aRange, - uint32_t* aOffset, - LineBreakType aLineBreakType) +nsresult +ContentEventHandler::GetFlatTextLengthBefore(nsRange* aRange, + uint32_t* aOffset, + LineBreakType aLineBreakType) { - nsINode* startNode = aRange->GetStartParent(); - NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); - int32_t startOffset = aRange->StartOffset(); - return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset, - aOffset, aLineBreakType); + MOZ_ASSERT(aRange); + return GetFlatTextLengthInRange( + NodePosition(mRootContent, 0), + NodePosition(aRange->GetStartParent(), aRange->StartOffset()), + mRootContent, aOffset, aLineBreakType); } nsresult diff --git a/dom/events/ContentEventHandler.h b/dom/events/ContentEventHandler.h index ffd73437a860..3957f2eb281e 100644 --- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -10,6 +10,8 @@ #include "mozilla/EventForwards.h" #include "mozilla/dom/Selection.h" #include "nsCOMPtr.h" +#include "nsIFrame.h" +#include "nsINode.h" #include "nsRange.h" class nsPresContext; @@ -81,15 +83,60 @@ public: // FlatText means the text that is generated from DOM tree. The BR elements // are replaced to native linefeeds. Other elements are ignored. - // Get the offset in FlatText of the range. (also used by IMEContentObserver) - static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsINode* aNode, - int32_t aNodeOffset, - uint32_t* aOffset, - LineBreakType aLineBreakType); - static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsRange* aRange, - uint32_t* aOffset, + // NodePosition stores a pair of node and offset in the node. + // This is useful to receive one or more sets of them instead of nsRange. + struct NodePosition final + { + nsCOMPtr mNode; + int32_t mOffset; + + NodePosition() + : mOffset(-1) + { + } + + NodePosition(nsINode* aNode, int32_t aOffset) + : mNode(aNode) + , mOffset(aOffset) + { + } + + explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets) + : mNode(aContentOffsets.content) + , mOffset(aContentOffsets.offset) + { + } + + bool IsValid() const + { + return mNode && mOffset >= 0; + } + bool OffsetIsValid() const + { + return IsValid() && static_cast(mOffset) <= mNode->Length(); + } + nsresult SetToRangeStart(nsRange* aRange) const + { + nsCOMPtr domNode(do_QueryInterface(mNode)); + return aRange->SetStart(domNode, mOffset); + } + nsresult SetToRangeEnd(nsRange* aRange) const + { + nsCOMPtr domNode(do_QueryInterface(mNode)); + return aRange->SetEnd(domNode, mOffset); + } + nsresult SetToRangeEndAfter(nsRange* aRange) const + { + nsCOMPtr domNode(do_QueryInterface(mNode)); + return aRange->SetEndAfter(domNode); + } + }; + + // Get the flatten text length in the range. + static nsresult GetFlatTextLengthInRange(const NodePosition& aStartPosition, + const NodePosition& aEndPosition, + nsIContent* aRootContent, + uint32_t* aLength, LineBreakType aLineBreakType); // Computes the native text length between aStartOffset and aEndOffset of // aContent. aContent must be a text node. @@ -114,6 +161,14 @@ protected: uint32_t aXPStartOffset, uint32_t aXPEndOffset, LineBreakType aLineBreakType); + // Get the contents of aRange as plain text. + nsresult GenerateFlatTextContent(nsRange* aRange, + nsAFlatString& aString, + LineBreakType aLineBreakType); + // Get the text length before the start position of aRange. + nsresult GetFlatTextLengthBefore(nsRange* aRange, + uint32_t* aOffset, + LineBreakType aLineBreakType); // Get the line breaker length. static inline uint32_t GetBRLength(LineBreakType aLineBreakType); static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent); diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index edbe236b12cd..f683694b9cb1 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -876,11 +876,15 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument, uint32_t offset = 0; // get offsets of change and fire notification nsresult rv = - ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContent, - aInfo->mChangeStart, - &offset, - LINE_BREAK_TYPE_NATIVE); - NS_ENSURE_SUCCESS_VOID(rv); + ContentEventHandler::GetFlatTextLengthInRange( + NodePosition(mRootContent, 0), + NodePosition(aContent, aInfo->mChangeStart), + mRootContent, + &offset, + LINE_BREAK_TYPE_NATIVE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } uint32_t newLength = ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart, @@ -912,9 +916,11 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer, nsresult rv = NS_OK; if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) { mEndOfAddedTextCache.Clear(); - rv = ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContainer, - aStartIndex, &offset, - LINE_BREAK_TYPE_NATIVE); + rv = ContentEventHandler::GetFlatTextLengthInRange( + NodePosition(mRootContent, 0), + NodePosition(aContainer, aStartIndex), + mRootContent, &offset, + LINE_BREAK_TYPE_NATIVE); if (NS_WARN_IF(NS_FAILED((rv)))) { return; } @@ -923,11 +929,12 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer, } // get offset at the end of the last added node - nsIContent* childAtStart = aContainer->GetChildAt(aStartIndex); uint32_t addingLength = 0; - rv = ContentEventHandler::GetFlatTextOffsetOfRange(childAtStart, aContainer, - aEndIndex, &addingLength, - LINE_BREAK_TYPE_NATIVE); + rv = ContentEventHandler::GetFlatTextLengthInRange( + NodePosition(aContainer, aStartIndex), + NodePosition(aContainer, aEndIndex), + mRootContent, &addingLength, + LINE_BREAK_TYPE_NATIVE); if (NS_WARN_IF(NS_FAILED((rv)))) { mEndOfAddedTextCache.Clear(); return; @@ -988,10 +995,12 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument, uint32_t offset = 0; nsresult rv = NS_OK; if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) { - rv = - ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, containerNode, - aIndexInContainer, &offset, - LINE_BREAK_TYPE_NATIVE); + rv = ContentEventHandler::GetFlatTextLengthInRange( + NodePosition(mRootContent, 0), + NodePosition(containerNode, aIndexInContainer), + mRootContent, + &offset, + LINE_BREAK_TYPE_NATIVE); if (NS_WARN_IF(NS_FAILED(rv))) { mStartOfRemovingTextRangeCache.Clear(); return; @@ -1003,18 +1012,21 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument, } // get offset at the end of the deleted node - int32_t nodeLength = - aChild->IsNodeOfType(nsINode::eTEXT) ? - static_cast(aChild->TextLength()) : - std::max(static_cast(aChild->GetChildCount()), 1); - MOZ_ASSERT(nodeLength >= 0, "The node length is out of range"); uint32_t textLength = 0; - rv = ContentEventHandler::GetFlatTextOffsetOfRange(aChild, aChild, - nodeLength, &textLength, - LINE_BREAK_TYPE_NATIVE); - if (NS_WARN_IF(NS_FAILED(rv))) { - mStartOfRemovingTextRangeCache.Clear(); - return; + if (aChild->IsNodeOfType(nsINode::eTEXT)) { + textLength = ContentEventHandler::GetNativeTextLength(aChild); + } else { + uint32_t nodeLength = + std::max(static_cast(aChild->GetChildCount()), 1); + rv = ContentEventHandler::GetFlatTextLengthInRange( + NodePosition(aChild, 0), + NodePosition(aChild, nodeLength), + mRootContent, &textLength, + LINE_BREAK_TYPE_NATIVE); + if (NS_WARN_IF(NS_FAILED(rv))) { + mStartOfRemovingTextRangeCache.Clear(); + return; + } } if (!textLength) { @@ -1062,10 +1074,14 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument, } uint32_t start; nsresult rv = - ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aElement, - 0, &start, + ContentEventHandler::GetFlatTextLengthInRange(NodePosition(mRootContent, 0), + NodePosition(aElement, 0), + mRootContent, + &start, LINE_BREAK_TYPE_NATIVE); - NS_ENSURE_SUCCESS_VOID(rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } TextChangeData data(start, start + mPreAttrChangeLength, start + postAttrChangeLength, causedByComposition, diff --git a/dom/events/IMEContentObserver.h b/dom/events/IMEContentObserver.h index e9bae8b134d1..a6d13cfa1a89 100644 --- a/dom/events/IMEContentObserver.h +++ b/dom/events/IMEContentObserver.h @@ -40,6 +40,7 @@ class IMEContentObserver final : public nsISelectionListener , public nsIEditorObserver { public: + typedef ContentEventHandler::NodePosition NodePosition; typedef widget::IMENotification::SelectionChangeData SelectionChangeData; typedef widget::IMENotification::TextChangeData TextChangeData; typedef widget::IMENotification::TextChangeDataBase TextChangeDataBase; From ae9603b7824357505fdf38b90263cc87f02527bb Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 2 Dec 2015 13:20:00 +0900 Subject: [PATCH 078/102] Bug 1213589 part.3 ContentEventHandler::GetFlatTextLengthInRange() should handle specially when it's called by nsIMutationObserver::ContentRemoved() r=smaug --- dom/events/ContentEventHandler.cpp | 82 ++++++++++++++++++++---------- dom/events/ContentEventHandler.h | 5 +- dom/events/IMEContentObserver.cpp | 5 +- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index b6e54a7fe05b..1cc05d7a4e5f 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -1442,46 +1442,76 @@ ContentEventHandler::GetFlatTextLengthInRange( const NodePosition& aEndPosition, nsIContent* aRootContent, uint32_t* aLength, - LineBreakType aLineBreakType) + LineBreakType aLineBreakType, + bool aIsRemovingNode /* = false */) { if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) || NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) { return NS_ERROR_INVALID_ARG; } - RefPtr prev = new nsRange(aRootContent); - nsresult rv = aStartPosition.SetToRangeStart(prev); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + // Don't create nsContentIterator instance until it's really necessary since + // destroying without initializing causes unexpected NS_ASSERTION() call. + nsCOMPtr iter; - nsCOMPtr iter = NS_NewContentIterator(); - if (aEndPosition.OffsetIsValid()) { - // Offset is within node's length; set end of range to that offset - rv = aEndPosition.SetToRangeEnd(prev); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = iter->Init(prev); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else if (aEndPosition.mNode != aRootContent) { - // Offset is past node's length; set end of range to end of node - rv = aEndPosition.SetToRangeEndAfter(prev); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = iter->Init(prev); + // This may be called for retrieving the text of removed nodes. Even in this + // case, the node thinks it's still in the tree because UnbindFromTree() will + // be called after here. However, the node was already removed from the + // array of children of its parent. So, be careful to handle this case. + if (aIsRemovingNode) { + DebugOnly parent = aStartPosition.mNode->GetParent(); + MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1, + "At removing the node, the node shouldn't be in the array of children " + "of its parent"); + MOZ_ASSERT(aStartPosition.mNode == aEndPosition.mNode, + "At removing the node, start and end node should be same"); + MOZ_ASSERT(aStartPosition.mOffset == 0, + "When the node is being removed, the start offset should be 0"); + MOZ_ASSERT(static_cast(aEndPosition.mOffset) == + aEndPosition.mNode->GetChildCount(), + "When the node is being removed, the end offset should be child count"); + iter = NS_NewContentIterator(); + nsresult rv = iter->Init(aStartPosition.mNode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { - // Offset is past the root node; set end of range to end of root node - rv = iter->Init(aRootContent); + RefPtr prev = new nsRange(aRootContent); + nsresult rv = aStartPosition.SetToRangeStart(prev); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (aEndPosition.OffsetIsValid()) { + // Offset is within node's length; set end of range to that offset + rv = aEndPosition.SetToRangeEnd(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + iter = NS_NewContentIterator(); + rv = iter->Init(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else if (aEndPosition.mNode != aRootContent) { + // Offset is past node's length; set end of range to end of node + rv = aEndPosition.SetToRangeEndAfter(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + iter = NS_NewPreContentIterator(); + rv = iter->Init(prev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + // Offset is past the root node; set end of range to end of root node + iter = NS_NewPreContentIterator(); + rv = iter->Init(aRootContent); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } } *aLength = 0; diff --git a/dom/events/ContentEventHandler.h b/dom/events/ContentEventHandler.h index 3957f2eb281e..a55df3fcbd2f 100644 --- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -133,11 +133,14 @@ public: }; // Get the flatten text length in the range. + // @param aIsRemovingNode Should be true only when this is called from + // nsIMutationObserver::ContentRemoved(). static nsresult GetFlatTextLengthInRange(const NodePosition& aStartPosition, const NodePosition& aEndPosition, nsIContent* aRootContent, uint32_t* aLength, - LineBreakType aLineBreakType); + LineBreakType aLineBreakType, + bool aIsRemovingNode = false); // Computes the native text length between aStartOffset and aEndOffset of // aContent. aContent must be a text node. static uint32_t GetNativeTextLength(nsIContent* aContent, diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index f683694b9cb1..264e251189f9 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -1016,13 +1016,12 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument, if (aChild->IsNodeOfType(nsINode::eTEXT)) { textLength = ContentEventHandler::GetNativeTextLength(aChild); } else { - uint32_t nodeLength = - std::max(static_cast(aChild->GetChildCount()), 1); + uint32_t nodeLength = static_cast(aChild->GetChildCount()); rv = ContentEventHandler::GetFlatTextLengthInRange( NodePosition(aChild, 0), NodePosition(aChild, nodeLength), mRootContent, &textLength, - LINE_BREAK_TYPE_NATIVE); + LINE_BREAK_TYPE_NATIVE, true); if (NS_WARN_IF(NS_FAILED(rv))) { mStartOfRemovingTextRangeCache.Clear(); return; From 6edaa422d8748a6194bebca0c2ad77739ed10c67 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 2 Dec 2015 13:20:00 +0900 Subject: [PATCH 079/102] Bug 1213859 part.4 ContentEventHandler should use NS_NewPreContentIterator rathr than NS_NewContentIterator at converting between DOM tree and flattened text r=smaug --- dom/events/ContentEventHandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 1cc05d7a4e5f..7c3495f2c9e9 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -428,7 +428,7 @@ ContentEventHandler::GenerateFlatTextContent(nsRange* aRange, return NS_OK; } - nsCOMPtr iter = NS_NewContentIterator(); + nsCOMPtr iter = NS_NewPreContentIterator(); nsresult rv = iter->Init(aRange); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -588,7 +588,7 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange, // baseOffset is the flattened offset of each content node. int32_t baseOffset = 0; - nsCOMPtr iter = NS_NewContentIterator(); + nsCOMPtr iter = NS_NewPreContentIterator(); nsresult rv = iter->Init(aRange); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -1470,7 +1470,7 @@ ContentEventHandler::GetFlatTextLengthInRange( MOZ_ASSERT(static_cast(aEndPosition.mOffset) == aEndPosition.mNode->GetChildCount(), "When the node is being removed, the end offset should be child count"); - iter = NS_NewContentIterator(); + iter = NS_NewPreContentIterator(); nsresult rv = iter->Init(aStartPosition.mNode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -1488,7 +1488,7 @@ ContentEventHandler::GetFlatTextLengthInRange( if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - iter = NS_NewContentIterator(); + iter = NS_NewPreContentIterator(); rv = iter->Init(prev); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; From 661b0d48d557c2398483e9994f312555f379b2b6 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 2 Dec 2015 13:20:00 +0900 Subject: [PATCH 080/102] Bug 1213589 part.5 Redesign the rules to create range in ContentEventHandler::SetRangeFromFlatTextOffset() r=smaug --- dom/events/ContentEventHandler.cpp | 241 +++++-- dom/events/ContentEventHandler.h | 5 + .../window_composition_text_querycontent.xul | 672 +++++++++--------- 3 files changed, 526 insertions(+), 392 deletions(-) diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 7c3495f2c9e9..3a902a4bc610 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -40,6 +40,56 @@ using namespace widget; /* ContentEventHandler */ /******************************************************************/ +// NOTE +// +// ContentEventHandler *creates* ranges as following rules: +// 1. Start of range: +// 1.1. Cases: [textNode or text[Node or textNode[ +// When text node is start of a range, start node is the text node and +// start offset is any number between 0 and the length of the text. +// 1.2. Case: [ +// When before an element node is start of a range, start node is parent +// of the element and start offset is the element's index in the parent. +// 1.3. Case: [ +// When after an empty element node is start of a range, start node is +// parent of the element and start offset is the element's index in the +// parent + 1. +// 1.4. Case: [ +// When start of a non-empty element is start of a range, start node is +// the element and start offset is 0. +// 1.5. Case: [ +// When start of a range is out of bounds, start node is the root node +// and start offset is number of the children. +// 2. End of range: +// 2.1. Cases: ]textNode or text]Node or textNode] +// When a text node is end of a range, end node is the text node and +// end offset is any number between 0 and the length of the text. +// 2.2. Case: ] +// When before an element node (meaning before the open tag of the +// element) is end of a range, end node is previous node causing text. +// Note that this case shouldn't be handled directly. If rule 2.1 and +// 2.3 are handled correctly, the loop with nsContentIterator shouldn't +// reach the element node since the loop should've finished already at +// handling the last node which caused some text. +// 2.3. Case: ] +// When after an element node is end of a range, end node is parent of +// the element node and end offset is the element's index in the parent +// + 1. +// 2.4. Case: ] +// When end of a range is out of bounds, end node is the root node and +// end offset is number of the children. +// +// ContentEventHandler *treats* ranges as following additional rules: +// 1. When the start node is an element node which doesn't have children, +// it includes a line break caused before itself (i.e., includes its open +// tag). For example, if start position is {
, 0 }, the line break +// caused by
should be included into the flatten text. +// 2. When the end node is an element node which doesn't have children, +// it includes the end (i.e., includes its close tag except empty element). +// Although, currently, any close tags don't cause line break, this also +// includes its open tag. For example, if end position is {
, 0 }, the +// line break caused by the
should be included into the flatten text. + ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext) : mPresContext(aPresContext) , mPresShell(aPresContext->GetPresShell()) @@ -414,6 +464,10 @@ ContentEventHandler::GenerateFlatTextContent(nsRange* aRange, { NS_ASSERTION(aString.IsEmpty(), "aString must be empty string"); + if (aRange->Collapsed()) { + return NS_OK; + } + nsINode* startNode = aRange->GetStartParent(); nsINode* endNode = aRange->GetEndParent(); if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { @@ -580,6 +634,10 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange, { MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array"); + if (aRange->Collapsed()) { + return NS_OK; + } + nsINode* startNode = aRange->GetStartParent(); nsINode* endNode = aRange->GetEndParent(); if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { @@ -696,6 +754,18 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, *aNewOffset = aOffset; } + // Special case like
+ if (!mRootContent->HasChildren()) { + nsresult rv = aRange->SetStart(mRootContent, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = aRange->SetEnd(mRootContent, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + nsCOMPtr iter = NS_NewPreContentIterator(); nsresult rv = iter->Init(mRootContent); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -710,7 +780,8 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, if (NS_WARN_IF(!node)) { break; } - if (!node->IsContent()) { + // FYI: mRootContent shouldn't cause any text. So, we can skip it simply. + if (node == mRootContent || !node->IsContent()) { continue; } nsIContent* content = node->AsContent(); @@ -723,12 +794,14 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, continue; } - if (offset <= aOffset && aOffset < offset + textLength) { - uint32_t xpOffset; - if (!content->IsNodeOfType(nsINode::eTEXT)) { - xpOffset = 0; - } else { - xpOffset = aOffset - offset; + // When the start offset is in between accumulated offset and the last + // offset of the node, the node is the start node of the range. + if (!startSet && aOffset <= offset + textLength) { + nsINode* startNode = nullptr; + int32_t startNodeOffset = -1; + if (content->IsNodeOfType(nsINode::eTEXT)) { + // Rule #1.1: [textNode or text[Node or textNode[ + uint32_t xpOffset = aOffset - offset; if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { xpOffset = ConvertToXPOffset(content, xpOffset); } @@ -744,27 +817,61 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, *aNewOffset -= (oldXPOffset - xpOffset); } } + startNode = content; + startNodeOffset = static_cast(xpOffset); + } else if (aOffset < offset + textLength) { + // Rule #1.2 [ + startNode = content->GetParent(); + if (NS_WARN_IF(!startNode)) { + return NS_ERROR_FAILURE; + } + startNodeOffset = startNode->IndexOf(content); + if (NS_WARN_IF(startNodeOffset == -1)) { + // The content is being removed from the parent! + return NS_ERROR_FAILURE; + } + } else if (!content->HasChildren()) { + // Rule #1.3: [ + startNode = content->GetParent(); + if (NS_WARN_IF(!startNode)) { + return NS_ERROR_FAILURE; + } + startNodeOffset = startNode->IndexOf(content) + 1; + if (NS_WARN_IF(startNodeOffset == 0)) { + // The content is being removed from the parent! + return NS_ERROR_FAILURE; + } + } else { + // Rule #1.4: [ + startNode = content; + startNodeOffset = 0; } - - rv = aRange->SetStart(content, int32_t(xpOffset)); + NS_ASSERTION(startNode, "startNode must not be nullptr"); + NS_ASSERTION(startNodeOffset >= 0, + "startNodeOffset must not be negative"); + rv = aRange->SetStart(startNode, startNodeOffset); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } startSet = true; - if (aLength == 0) { - // Ensure that the end offset and the start offset are same. - rv = aRange->SetEnd(content, int32_t(xpOffset)); + + if (!aLength) { + rv = aRange->SetEnd(startNode, startNodeOffset); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } } + + // When the end offset is in the content, the node is the end node of the + // range. if (endOffset <= offset + textLength) { - nsINode* endNode = content; - uint32_t xpOffset; + MOZ_ASSERT(startSet, + "The start of the range should've been set already"); if (content->IsNodeOfType(nsINode::eTEXT)) { - xpOffset = endOffset - offset; + // Rule #2.1: ]textNode or text]Node or textNode] + uint32_t xpOffset = endOffset - offset; if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { xpOffset = ConvertToXPOffset(content, xpOffset); } @@ -774,18 +881,36 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, return rv; } } - } else { - // Use first position of next node, because the end node is ignored - // by ContentIterator when the offset is zero. - xpOffset = 0; - iter->Next(); - if (iter->IsDone()) { - break; + NS_ASSERTION(xpOffset <= INT32_MAX, + "The end node offset is too large"); + rv = aRange->SetEnd(content, static_cast(xpOffset)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - endNode = iter->GetCurrentNode(); + return NS_OK; } - rv = aRange->SetEnd(endNode, int32_t(xpOffset)); + if (endOffset == offset) { + // Rule #2.2: ] + // NOTE: Please don't crash on release builds because it must be + // overreaction but we shouldn't allow this bug when some + // automated tests find this. + MOZ_ASSERT(false, "This case should've already been handled at " + "the last node which caused some text"); + return NS_ERROR_FAILURE; + } + + // Rule #2.3: ] + nsINode* endNode = content->GetParent(); + if (NS_WARN_IF(!endNode)) { + return NS_ERROR_FAILURE; + } + int32_t indexInParent = endNode->IndexOf(content); + if (NS_WARN_IF(indexInParent == -1)) { + // The content is being removed from the parent! + return NS_ERROR_FAILURE; + } + rv = aRange->SetEnd(endNode, indexInParent + 1); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -795,13 +920,11 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, offset += textLength; } - if (offset < aOffset) { - return NS_ERROR_FAILURE; - } - if (!startSet) { + // Rule #1.5: [ MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); - rv = aRange->SetStart(mRootContent, int32_t(mRootContent->GetChildCount())); + rv = aRange->SetStart(mRootContent, + static_cast(mRootContent->GetChildCount())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -809,7 +932,9 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, *aNewOffset = offset; } } - rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount())); + // Rule #2.4: ] + rv = aRange->SetEnd(mRootContent, + static_cast(mRootContent->GetChildCount())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1450,6 +1575,11 @@ ContentEventHandler::GetFlatTextLengthInRange( return NS_ERROR_INVALID_ARG; } + if (aStartPosition == aEndPosition) { + *aLength = 0; + return NS_OK; + } + // Don't create nsContentIterator instance until it's really necessary since // destroying without initializing causes unexpected NS_ASSERTION() call. nsCOMPtr iter; @@ -1655,33 +1785,36 @@ static void AdjustRangeForSelection(nsIContent* aRoot, { nsINode* node = *aNode; int32_t nodeOffset = *aNodeOffset; - if (aRoot != node && node->GetParent()) { - if (node->IsNodeOfType(nsINode::eTEXT)) { - // When the offset is at the end of the text node, set it to after the - // text node, to make sure the caret is drawn on a new line when the last - // character of the text node is '\n' - int32_t nodeLength = - static_cast(static_cast(node)->TextLength()); - MOZ_ASSERT(nodeOffset <= nodeLength, "Offset is past length of text node"); - if (nodeOffset == nodeLength) { - node = node->GetParent(); - nodeOffset = node->IndexOf(*aNode) + 1; - } - } else { - node = node->GetParent(); - nodeOffset = node->IndexOf(*aNode) + (nodeOffset ? 1 : 0); - } + if (aRoot == node || NS_WARN_IF(!node->GetParent()) || + !node->IsNodeOfType(nsINode::eTEXT)) { + return; } - nsIContent* brContent = node->GetChildAt(nodeOffset - 1); - while (brContent && brContent->IsHTMLElement()) { - if (!brContent->IsHTMLElement(nsGkAtoms::br) || IsContentBR(brContent)) { - break; - } - brContent = node->GetChildAt(--nodeOffset - 1); + // When the offset is at the end of the text node, set it to after the + // text node, to make sure the caret is drawn on a new line when the last + // character of the text node is '\n' in