For WinRT, add EGLRenderSurfaceScaleProperty to specify swapchain scaling factor

Change-Id: Iede6682306082346cf2a3a55fe58f732b7a453df
Reviewed-on: https://chromium-review.googlesource.com/277931
Tested-by: Austin Kinross <aukinros@microsoft.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Austin Kinross 2015-06-16 17:00:11 -07:00 коммит произвёл Jamie Madill
Родитель ce39f6ff93
Коммит da0564680b
9 изменённых файлов: 590 добавлений и 121 удалений

Просмотреть файл

@ -28,10 +28,24 @@ const wchar_t EGLNativeWindowTypeProperty[] = L"EGLNativeWindowTypeProperty";
// Description: Set this property to specify a preferred size in pixels of the render surface.
// The render surface size width and height must be greater than 0.
// If this property is set, then the render surface size is fixed.
// The render surface will then be scaled to the window dimensions.
// If this property is missing, a default behavior will be provided.
// The default behavior uses the window size if a CoreWindow is specified or
// the size of the SwapChainPanel control if one is specified.
//
const wchar_t EGLRenderSurfaceSizeProperty[] = L"EGLRenderSurfaceSizeProperty";
//
// Property: EGLRenderResolutionScaleProperty
// Type: Single
// Description: Use this to specify a preferred scale for the render surface compared to the window.
// For example, if the window is 800x480, and:
// - scale is set to 0.5f then the surface will be 400x240
// - scale is set to 1.2f then the surface will be 960x576
// If the window resizes or rotates then the surface will resize accordingly.
// EGLRenderResolutionScaleProperty and EGLRenderSurfaceSizeProperty cannot both be set.
// The scale factor should be > 0.0f.
//
const wchar_t EGLRenderResolutionScaleProperty[] = L"EGLRenderResolutionScaleProperty";
#endif // ANGLE_WINDOWSSTORE_H_

Просмотреть файл

@ -24,7 +24,6 @@ bool CoreWindowNativeWindow::initialize(EGLNativeWindowType window, IPropertySet
ComPtr<IPropertySet> props = propertySet;
ComPtr<IInspectable> win = window;
SIZE swapChainSize = {};
bool swapChainSizeSpecified = false;
HRESULT result = S_OK;
// IPropertySet is an optional parameter and can be null.
@ -33,12 +32,40 @@ bool CoreWindowNativeWindow::initialize(EGLNativeWindowType window, IPropertySet
if (propertySet)
{
result = props.As(&mPropertyMap);
if (SUCCEEDED(result))
if (FAILED(result))
{
// The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &swapChainSizeSpecified);
return false;
}
// The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &mSwapChainSizeSpecified);
if (FAILED(result))
{
return false;
}
// The EGLRenderResolutionScaleProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSinglePropertyValue(mPropertyMap, EGLRenderResolutionScaleProperty, &mSwapChainScale, &mSwapChainScaleSpecified);
if (FAILED(result))
{
return false;
}
if (!mSwapChainScaleSpecified)
{
// Default value for the scale is 1.0f
mSwapChainScale = 1.0f;
}
// A EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty can't both be specified
if (mSwapChainScaleSpecified && mSwapChainSizeSpecified)
{
ERR("It is invalid to specify both an EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty.");
return false;
}
}
@ -55,14 +82,19 @@ bool CoreWindowNativeWindow::initialize(EGLNativeWindowType window, IPropertySet
// of the host.
// Scaling of the swapchain output occurs automatically because if
// the scaling mode setting DXGI_SCALING_STRETCH on the swapchain.
if (swapChainSizeSpecified)
if (mSwapChainSizeSpecified)
{
mClientRect = { 0, 0, swapChainSize.cx, swapChainSize.cy };
mSupportsSwapChainResize = false;
}
else
{
result = GetCoreWindowSizeInPixels(mCoreWindow, &mClientRect);
SIZE coreWindowSize;
result = GetCoreWindowSizeInPixels(mCoreWindow, &coreWindowSize);
if (SUCCEEDED(result))
{
mClientRect = { 0, 0, static_cast<long>(coreWindowSize.cx * mSwapChainScale), static_cast<long>(coreWindowSize.cy * mSwapChainScale) };
}
}
}
@ -154,13 +186,20 @@ HRESULT CoreWindowNativeWindow::createSwapChain(ID3D11Device *device, DXGIFactor
return result;
}
HRESULT GetCoreWindowSizeInPixels(const ComPtr<ABI::Windows::UI::Core::ICoreWindow>& coreWindow, RECT *windowSize)
inline HRESULT CoreWindowNativeWindow::scaleSwapChain(const SIZE &windowSize, const RECT &clientRect)
{
// We don't need to do any additional work to scale CoreWindow swapchains.
// Using DXGI_SCALING_STRETCH to create the swapchain above does all the necessary work.
return S_OK;
}
HRESULT GetCoreWindowSizeInPixels(const ComPtr<ABI::Windows::UI::Core::ICoreWindow>& coreWindow, SIZE *windowSize)
{
ABI::Windows::Foundation::Rect bounds;
HRESULT result = coreWindow->get_Bounds(&bounds);
if (SUCCEEDED(result))
{
*windowSize = { 0, 0, ConvertDipsToPixels(bounds.Width), ConvertDipsToPixels(bounds.Height) };
*windowSize = { ConvertDipsToPixels(bounds.Width), ConvertDipsToPixels(bounds.Height) };
}
return result;

Просмотреть файл

@ -24,10 +24,14 @@ class CoreWindowNativeWindow : public InspectableNativeWindow, public std::enabl
public:
~CoreWindowNativeWindow();
bool initialize(EGLNativeWindowType window, IPropertySet *propertySet);
bool initialize(EGLNativeWindowType window, IPropertySet *propertySet) override;
HRESULT createSwapChain(ID3D11Device *device, DXGIFactory *factory, DXGI_FORMAT format, unsigned int width, unsigned int height, DXGISwapChain **swapChain) override;
protected:
HRESULT scaleSwapChain(const SIZE &windowSize, const RECT &clientRect) override;
bool registerForSizeChangeEvents();
void unregisterForSizeChangeEvents();
HRESULT createSwapChain(ID3D11Device *device, DXGIFactory *factory, DXGI_FORMAT format, unsigned int width, unsigned int height, DXGISwapChain **swapChain);
private:
ComPtr<ABI::Windows::UI::Core::ICoreWindow> mCoreWindow;
@ -72,7 +76,7 @@ class CoreWindowSizeChangedHandler :
std::weak_ptr<InspectableNativeWindow> mHost;
};
HRESULT GetCoreWindowSizeInPixels(const ComPtr<ABI::Windows::UI::Core::ICoreWindow>& coreWindow, RECT *windowSize);
HRESULT GetCoreWindowSizeInPixels(const ComPtr<ABI::Windows::UI::Core::ICoreWindow>& coreWindow, SIZE *windowSize);
}
#endif // LIBANGLE_RENDERER_D3D_D3D11_WINRT_COREWINDOWNATIVEWINDOW_H_

Просмотреть файл

@ -162,12 +162,35 @@ HRESULT CreatePropertyMap(IMap<HSTRING, IInspectable*>** propertyMap)
return result;
}
HRESULT CreatePropertyValueStatics(IPropertyValueStatics** propertyStatics)
{
ComPtr<IPropertyValueStatics> propertyValueStatics;
HRESULT result = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propertyValueStatics);
EXPECT_HRESULT_SUCCEEDED(result);
result = propertyValueStatics.CopyTo(propertyStatics);
EXPECT_HRESULT_SUCCEEDED(result);
return result;
}
HRESULT SetInspectablePropertyValue(const ComPtr<IMap<HSTRING, IInspectable*>>& propertyMap, const wchar_t* propertyName, IInspectable* inspectable)
{
boolean propertyReplaced = false;
return propertyMap->Insert(HStringReference(propertyName).Get(), inspectable, &propertyReplaced);
}
void expectNativeWindowInitCalls(MockCoreWindow &coreWindow, bool expectBounds)
{
if (expectBounds)
{
EXPECT_CALL(coreWindow, get_Bounds(testing::_)).Times(1);
}
EXPECT_CALL(coreWindow, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(coreWindow, remove_SizeChanged(testing::_)).Times(1);
}
TEST(NativeWindowTest, NativeWindowNull)
{
NativeWindow nativeWindow(nullptr);
@ -191,9 +214,7 @@ TEST(NativeWindowTest, NativeWindowNotInspectable)
TEST(NativeWindowTest, NativeWindowValidCoreWindow)
{
MockCoreWindow mockCoreWindow;
EXPECT_CALL(mockCoreWindow, get_Bounds(testing::_)).Times(1);
EXPECT_CALL(mockCoreWindow, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(mockCoreWindow, remove_SizeChanged(testing::_)).Times(1);
expectNativeWindowInitCalls(mockCoreWindow, true);
NativeWindow nativeWindow(&mockCoreWindow);
EXPECT_TRUE(nativeWindow.initialize());
}
@ -205,11 +226,12 @@ TEST(NativeWindowTest, NativeWindowValidCoreWindowInPropertySet)
{
MockCoreWindow mockCoreWindow;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
EXPECT_CALL(mockCoreWindow, get_Bounds(testing::_)).Times(1);
EXPECT_CALL(mockCoreWindow, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(mockCoreWindow, remove_SizeChanged(testing::_)).Times(1);
// Add the CoreWindow to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, &mockCoreWindow));
expectNativeWindowInitCalls(mockCoreWindow, true);
NativeWindow nativeWindow(propertySet.Get());
EXPECT_TRUE(nativeWindow.initialize());
}
@ -223,11 +245,112 @@ TEST(NativeWindowTest, NativeWindowMissingCoreWindowInPropertySet)
{
MockCoreWindow mockCoreWindow;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
NativeWindow nativeWindow(propertySet.Get());
EXPECT_FALSE(nativeWindow.initialize());
}
CoUninitialize();
}
// Tests that the scale property works as expected in a property set with a SwapChainPanel
class CoreWindowScaleTest : public testing::TestWithParam<std::pair<float, bool>>
{
};
TEST_P(CoreWindowScaleTest, ValidateScale)
{
float scale = GetParam().first;
bool expectedResult = GetParam().second;
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockCoreWindow mockCoreWindow;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
ComPtr<IPropertyValueStatics> propertyValueStatics;
ComPtr<IPropertyValue> singleValue;
// Create a simple property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockCoreWindow)));
// Add a valid scale factor to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSingle(scale, reinterpret_cast<IInspectable**>(singleValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderResolutionScaleProperty, reinterpret_cast<IInspectable*>(singleValue.Get())));
// Check native window init status and calls to the mock swapchainpanel
NativeWindow nativeWindow(propertySet.Get());
if (expectedResult)
{
expectNativeWindowInitCalls(mockCoreWindow, true);
}
EXPECT_EQ(nativeWindow.initialize(), expectedResult);
}
CoUninitialize();
}
typedef std::pair<float, bool> scaleValidPair;
static const scaleValidPair scales[] = { scaleValidPair(1.0f, true),
scaleValidPair(0.5f, true),
scaleValidPair(0.0f, false),
scaleValidPair(0.01f, true),
scaleValidPair(2.00f, true) };
INSTANTIATE_TEST_CASE_P(NativeWindowTest,
CoreWindowScaleTest,
testing::ValuesIn(scales));
// Tests that the size property works as expected in a property set with a SwapChainPanel
class CoreWindowSizeTest : public testing::TestWithParam<std::tuple<float, float, bool>>
{
};
TEST_P(CoreWindowSizeTest, ValidateSize)
{
Size renderSize = { std::get<0>(GetParam()), std::get<1>(GetParam()) };
bool expectedResult = std::get<2>(GetParam());
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockCoreWindow mockCoreWindow;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
ComPtr<IPropertyValueStatics> propertyValueStatics;
ComPtr<IPropertyValue> sizeValue;
// Create a simple property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockCoreWindow)));
// Add a valid size to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSize(renderSize, reinterpret_cast<IInspectable**>(sizeValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderSurfaceSizeProperty, reinterpret_cast<IInspectable*>(sizeValue.Get())));
// Check native window init status and calls to the mock swapchainpanel
NativeWindow nativeWindow(propertySet.Get());
if (expectedResult)
{
expectNativeWindowInitCalls(mockCoreWindow, false);
}
EXPECT_EQ(nativeWindow.initialize(), expectedResult);
}
CoUninitialize();
}
typedef std::tuple<float, float, bool> sizeValidPair;
static const sizeValidPair sizes[] = { sizeValidPair( 800, 480, true),
sizeValidPair( 0, 480, false),
sizeValidPair( 800, 0, false),
sizeValidPair( 0, 0, false) };
INSTANTIATE_TEST_CASE_P(NativeWindowTest,
CoreWindowSizeTest,
testing::ValuesIn(sizes));
} // namespace

Просмотреть файл

@ -197,16 +197,47 @@ bool IsEGLConfiguredPropertySet(EGLNativeWindowType window, ABI::Windows::Founda
// A Valid EGLNativeWindowType IInspectable can only be:
//
// ICoreWindow
// ISwapChainPanel
// IPropertySet
//
//
// Anything else will be rejected as an invalid IInspectable.
bool IsValidEGLNativeWindowType(EGLNativeWindowType window)
{
return IsCoreWindow(window) || IsSwapChainPanel(window) || IsEGLConfiguredPropertySet(window);
}
// Retrieve an optional property from a property set
HRESULT GetOptionalPropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName,
boolean *hasKey,
ComPtr<ABI::Windows::Foundation::IPropertyValue> &propertyValue)
{
if (!propertyMap || !hasKey)
{
return E_INVALIDARG;
}
// Assume that the value does not exist
*hasKey = false;
HRESULT result = propertyMap->HasKey(HStringReference(propertyName).Get(), hasKey);
if (SUCCEEDED(result) && !(*hasKey))
{
// Value does not exist, so return S_OK and set the exists parameter to false to indicate
// that a the optional property does not exist.
return S_OK;
}
if (SUCCEEDED(result))
{
result = propertyMap->Lookup(HStringReference(propertyName).Get(), &propertyValue);
}
return result;
}
// Attempts to read an optional SIZE property value that is assumed to be in the form of
// an ABI::Windows::Foundation::Size. This function validates the Size value before returning
// an ABI::Windows::Foundation::Size. This function validates the Size value before returning
// it to the caller.
//
// Possible return values are:
@ -217,9 +248,15 @@ bool IsValidEGLNativeWindowType(EGLNativeWindowType window)
// * Invalid property value (width/height must be > 0)
// Additional errors may be returned from IMap or IPropertyValue
//
HRESULT GetOptionalSizePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>>& propertyMap, const wchar_t *propertyName, SIZE *value, bool *valueExists)
HRESULT GetOptionalSizePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName, SIZE *value, bool *valueExists)
{
if (!propertyMap || !propertyName || !value || !valueExists)
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType = ABI::Windows::Foundation::PropertyType::PropertyType_Empty;
Size sizeValue = { 0, 0 };
boolean hasKey = false;
if (!propertyMap || !value || !valueExists)
{
return E_INVALIDARG;
}
@ -228,52 +265,95 @@ HRESULT GetOptionalSizePropertyValue(const ComPtr<ABI::Windows::Foundation::Coll
*valueExists = false;
*value = { 0, 0 };
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType = ABI::Windows::Foundation::PropertyType::PropertyType_Empty;
Size sizeValue = { 0, 0 };
boolean hasKey = false;
HRESULT result = propertyMap->HasKey(HStringReference(propertyName).Get(), &hasKey);
if (SUCCEEDED(result) && !hasKey)
{
// Value does not exist, so return S_OK and set the exists parameter to false to indicate
// that a the optional property does not exist.
*valueExists = false;
return S_OK;
}
if (SUCCEEDED(result))
{
result = propertyMap->Lookup(HStringReference(propertyName).Get(), &propertyValue);
}
if (SUCCEEDED(result))
HRESULT result = GetOptionalPropertyValue(propertyMap, propertyName, &hasKey, propertyValue);
if (SUCCEEDED(result) && hasKey)
{
result = propertyValue->get_Type(&propertyType);
}
// Check if the expected Size property is of PropertyType_Size type.
if (SUCCEEDED(result) && propertyType == ABI::Windows::Foundation::PropertyType::PropertyType_Size)
{
if (SUCCEEDED(propertyValue->GetSize(&sizeValue)) && (sizeValue.Width > 0 && sizeValue.Height > 0))
// Check if the expected Size property is of PropertyType_Size type.
if (SUCCEEDED(result) && propertyType == ABI::Windows::Foundation::PropertyType::PropertyType_Size)
{
// A valid property value exists
*value = { static_cast<long>(sizeValue.Width), static_cast<long>(sizeValue.Height) };
*valueExists = true;
result = S_OK;
if (SUCCEEDED(propertyValue->GetSize(&sizeValue)) && (sizeValue.Width > 0 && sizeValue.Height > 0))
{
// A valid property value exists
*value = { static_cast<long>(sizeValue.Width), static_cast<long>(sizeValue.Height) };
*valueExists = true;
result = S_OK;
}
else
{
// An invalid Size property was detected. Width/Height values must > 0
result = E_INVALIDARG;
}
}
else
{
// An invalid Size property was detected. Width/Height values must > 0
// An invalid property type was detected. Size property must be of PropertyType_Size
result = E_INVALIDARG;
}
}
else
{
// An invalid property type was detected. Size property must be of PropertyType_Size
result = E_INVALIDARG;
}
return result;
}
// Attempts to read an optional float property value that is assumed to be in the form of
// an ABI::Windows::Foundation::Single. This function validates the Single value before returning
// it to the caller.
//
// Possible return values are:
// S_OK, valueExists == true - optional Single value was successfully retrieved and validated
// S_OK, valueExists == false - optional Single value was not found
// E_INVALIDARG, valueExists = false - optional Single value was malformed in the property set.
// * Incorrect property type ( must be PropertyType_Single)
// * Invalid property value (must be > 0)
// Additional errors may be returned from IMap or IPropertyValue
//
HRESULT GetOptionalSinglePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName, float *value, bool *valueExists)
{
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType = ABI::Windows::Foundation::PropertyType::PropertyType_Empty;
float scaleValue = 0.0f;
boolean hasKey = false;
if (!propertyMap || !value || !valueExists)
{
return E_INVALIDARG;
}
// Assume that the value does not exist
*valueExists = false;
*value = 0.0f;
HRESULT result = GetOptionalPropertyValue(propertyMap, propertyName, &hasKey, propertyValue);
if (SUCCEEDED(result) && hasKey)
{
result = propertyValue->get_Type(&propertyType);
// Check if the expected Scale property is of PropertyType_Single type.
if (SUCCEEDED(result) && propertyType == ABI::Windows::Foundation::PropertyType::PropertyType_Single)
{
if (SUCCEEDED(propertyValue->GetSingle(&scaleValue)) && (scaleValue > 0.0f))
{
// A valid property value exists
*value = scaleValue;
*valueExists = true;
result = S_OK;
}
else
{
// An invalid scale was set
result = E_INVALIDARG;
}
}
else
{
// An invalid property type was detected. Size property must be of PropertyType_Single
result = E_INVALIDARG;
}
}
return result;
}
}

Просмотреть файл

@ -31,7 +31,9 @@ class InspectableNativeWindow
public:
InspectableNativeWindow() :
mSupportsSwapChainResize(true),
mRequiresSwapChainScaling(false),
mSwapChainSizeSpecified(false),
mSwapChainScaleSpecified(false),
mSwapChainScale(1.0f),
mClientRectChanged(false),
mClientRect({0,0,0,0}),
mNewClientRect({0,0,0,0})
@ -42,13 +44,10 @@ class InspectableNativeWindow
virtual bool initialize(EGLNativeWindowType window, IPropertySet *propertySet) = 0;
virtual HRESULT createSwapChain(ID3D11Device *device, DXGIFactory *factory, DXGI_FORMAT format, unsigned int width, unsigned int height, DXGISwapChain **swapChain) = 0;
virtual bool registerForSizeChangeEvents() = 0;
virtual void unregisterForSizeChangeEvents() = 0;
virtual HRESULT scaleSwapChain(const SIZE& newSize) { return S_OK; }
bool getClientRect(RECT *rect)
{
if (mClientRectChanged && mSupportsSwapChainResize)
if (mClientRectChanged)
{
mClientRect = mNewClientRect;
}
@ -58,23 +57,44 @@ class InspectableNativeWindow
return true;
}
void setNewClientSize(const SIZE &newSize)
// setNewClientSize is used by the WinRT size change handler. It isn't used by the rest of ANGLE.
void setNewClientSize(const SIZE &newWindowSize)
{
if (mSupportsSwapChainResize && !mRequiresSwapChainScaling)
{
mNewClientRect = { 0, 0, newSize.cx, newSize.cy };
mClientRectChanged = true;
}
// If the client doesn't support swapchain resizing then we should have already unregistered from size change handler
ASSERT(mSupportsSwapChainResize);
if (mRequiresSwapChainScaling)
if (mSupportsSwapChainResize)
{
scaleSwapChain(newSize);
// If the swapchain size was specified then we should ignore this call too
if (!mSwapChainSizeSpecified)
{
// We don't have to check if a swapchain scale was specified here; the default value is 1.0f which will have no effect.
mNewClientRect = { 0, 0, static_cast<long>(newWindowSize.cx * mSwapChainScale), static_cast<long>(newWindowSize.cy * mSwapChainScale) };
mClientRectChanged = true;
// If a scale was specified, then now is the time to apply the scale matrix for the new swapchain size and window size
if (mSwapChainScaleSpecified)
{
scaleSwapChain(newWindowSize, mNewClientRect);
}
}
// Even if the swapchain size was fixed, the window might have changed size.
// In this case, we should recalculate the scale matrix to account for the new window size
if (mSwapChainSizeSpecified)
{
scaleSwapChain(newWindowSize, mClientRect);
}
}
}
protected:
bool mSupportsSwapChainResize;
bool mRequiresSwapChainScaling;
protected:
virtual HRESULT scaleSwapChain(const SIZE &windowSize, const RECT &clientRect) = 0;
bool mSupportsSwapChainResize; // Support for IDXGISwapChain::ResizeBuffers method
bool mSwapChainSizeSpecified; // If an EGLRenderSurfaceSizeProperty was specified
bool mSwapChainScaleSpecified; // If an EGLRenderResolutionScaleProperty was specified
float mSwapChainScale; // The scale value specified by the EGLRenderResolutionScaleProperty property
RECT mClientRect;
RECT mNewClientRect;
bool mClientRectChanged;
@ -86,8 +106,17 @@ bool IsValidEGLNativeWindowType(EGLNativeWindowType window);
bool IsCoreWindow(EGLNativeWindowType window, ComPtr<ABI::Windows::UI::Core::ICoreWindow> *coreWindow = nullptr);
bool IsSwapChainPanel(EGLNativeWindowType window, ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> *swapChainPanel = nullptr);
bool IsEGLConfiguredPropertySet(EGLNativeWindowType window, ABI::Windows::Foundation::Collections::IPropertySet **propertySet = nullptr, IInspectable **inspectable = nullptr);
HRESULT GetOptionalSizePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>>& propertyMap, const wchar_t *propertyName, SIZE *value, bool *valueExists);
HRESULT GetOptionalPropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName,
boolean *hasKey,
ComPtr<ABI::Windows::Foundation::IPropertyValue> &propertyValue);
HRESULT GetOptionalSizePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName, SIZE *value, bool *valueExists);
HRESULT GetOptionalSinglePropertyValue(const ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> &propertyMap,
const wchar_t *propertyName, float *value, bool *valueExists);
}
#endif // LIBANGLE_RENDERER_D3D_D3D11_WINRT_INSPECTABLENATIVEWINDOW_H_

Просмотреть файл

@ -25,7 +25,6 @@ bool SwapChainPanelNativeWindow::initialize(EGLNativeWindowType window, IPropert
ComPtr<IPropertySet> props = propertySet;
ComPtr<IInspectable> win = window;
SIZE swapChainSize = {};
bool swapChainSizeSpecified = false;
HRESULT result = S_OK;
// IPropertySet is an optional parameter and can be null.
@ -34,12 +33,40 @@ bool SwapChainPanelNativeWindow::initialize(EGLNativeWindowType window, IPropert
if (propertySet)
{
result = props.As(&mPropertyMap);
if (SUCCEEDED(result))
if (FAILED(result))
{
// The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &swapChainSizeSpecified);
return false;
}
// The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &mSwapChainSizeSpecified);
if (FAILED(result))
{
return false;
}
// The EGLRenderResolutionScaleProperty is optional and may be missing. The IPropertySet
// was prevalidated to contain the EGLNativeWindowType before being passed to
// this host.
result = GetOptionalSinglePropertyValue(mPropertyMap, EGLRenderResolutionScaleProperty, &mSwapChainScale, &mSwapChainScaleSpecified);
if (FAILED(result))
{
return false;
}
if (!mSwapChainScaleSpecified)
{
// Default value for the scale is 1.0f
mSwapChainScale = 1.0f;
}
// A EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty can't both be specified
if (mSwapChainScaleSpecified && mSwapChainSizeSpecified)
{
ERR("It is invalid to specify both an EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty.");
return false;
}
}
@ -57,16 +84,20 @@ bool SwapChainPanelNativeWindow::initialize(EGLNativeWindowType window, IPropert
// Scaling of the swapchain output needs to be handled by the
// host for swapchain panels even though the scaling mode setting
// DXGI_SCALING_STRETCH is configured on the swapchain.
if (swapChainSizeSpecified)
if (mSwapChainSizeSpecified)
{
mClientRect = { 0, 0, swapChainSize.cx, swapChainSize.cy };
// Enable host swapchain scaling
mRequiresSwapChainScaling = true;
}
else
{
result = GetSwapChainPanelSize(mSwapChainPanel, &mClientRect);
SIZE swapChainPanelSize;
result = GetSwapChainPanelSize(mSwapChainPanel, &swapChainPanelSize);
if (SUCCEEDED(result))
{
// Update the client rect to account for any swapchain scale factor
mClientRect = { 0, 0, static_cast<long>(swapChainPanelSize.cx * mSwapChainScale), static_cast<long>(swapChainPanelSize.cy * mSwapChainScale) };
}
}
}
@ -107,7 +138,7 @@ bool SwapChainPanelNativeWindow::registerForSizeChangeEvents()
void SwapChainPanelNativeWindow::unregisterForSizeChangeEvents()
{
ComPtr<ABI::Windows::UI::Xaml::IFrameworkElement> frameworkElement;
if (SUCCEEDED(mSwapChainPanel.As(&frameworkElement)))
if (mSwapChainPanel && SUCCEEDED(mSwapChainPanel.As(&frameworkElement)))
{
(void)frameworkElement->remove_SizeChanged(mSizeChangedEventToken);
}
@ -139,7 +170,7 @@ HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, DXGIFa
ComPtr<IDXGISwapChain1> newSwapChain;
ComPtr<ISwapChainPanelNative> swapChainPanelNative;
RECT currentPanelSize = {};
SIZE currentPanelSize = {};
HRESULT result = factory->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, newSwapChain.ReleaseAndGetAddressOf());
@ -165,34 +196,26 @@ HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, DXGIFa
// If the host is responsible for scaling the output of the swapchain, then
// scale it now before returning an instance to the caller. This is done by
// first reading the current size of the swapchain panel, then scaling
if (SUCCEEDED(result) && mRequiresSwapChainScaling)
{
result = GetSwapChainPanelSize(mSwapChainPanel, &currentPanelSize);
}
// Scale the swapchain to fit inside the contents of the panel.
if (SUCCEEDED(result) && mRequiresSwapChainScaling)
{
SIZE currentSize = { currentPanelSize.right, currentPanelSize.bottom };
result = scaleSwapChain(currentSize);
}
if (SUCCEEDED(result))
{
// If automatic swapchain resize behaviors have been disabled, then
// unregister for the resize change events.
if (mSupportsSwapChainResize == false)
if (mSwapChainSizeSpecified || mSwapChainScaleSpecified)
{
unregisterForSizeChangeEvents();
result = GetSwapChainPanelSize(mSwapChainPanel, &currentPanelSize);
// Scale the swapchain to fit inside the contents of the panel.
if (SUCCEEDED(result))
{
result = scaleSwapChain(currentPanelSize, mClientRect);
}
}
}
return result;
}
HRESULT SwapChainPanelNativeWindow::scaleSwapChain(const SIZE &newSize)
HRESULT SwapChainPanelNativeWindow::scaleSwapChain(const SIZE &windowSize, const RECT &clientRect)
{
ABI::Windows::Foundation::Size renderScale = { (float)newSize.cx/(float)mClientRect.right, (float)newSize.cy/(float)mClientRect.bottom };
ABI::Windows::Foundation::Size renderScale = { (float)windowSize.cx / (float)clientRect.right, (float)windowSize.cy / (float)clientRect.bottom };
// Setup a scale matrix for the swap chain
DXGI_MATRIX_3X2_F scaleMatrix = {};
scaleMatrix._11 = renderScale.Width;
@ -208,7 +231,7 @@ HRESULT SwapChainPanelNativeWindow::scaleSwapChain(const SIZE &newSize)
return result;
}
HRESULT GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel, RECT *windowSize)
HRESULT GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel, SIZE *windowSize)
{
ComPtr<ABI::Windows::UI::Xaml::IUIElement> uiElement;
ABI::Windows::Foundation::Size renderSize = { 0, 0 };
@ -220,7 +243,7 @@ HRESULT GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISw
if (SUCCEEDED(result))
{
*windowSize = { 0, 0, lround(renderSize.Width), lround(renderSize.Height) };
*windowSize = { lround(renderSize.Width), lround(renderSize.Height) };
}
return result;

Просмотреть файл

@ -18,11 +18,14 @@ class SwapChainPanelNativeWindow : public InspectableNativeWindow, public std::e
public:
~SwapChainPanelNativeWindow();
bool initialize(EGLNativeWindowType window, IPropertySet *propertySet);
bool initialize(EGLNativeWindowType window, IPropertySet *propertySet) override;
HRESULT createSwapChain(ID3D11Device *device, DXGIFactory *factory, DXGI_FORMAT format, unsigned int width, unsigned int height, DXGISwapChain **swapChain) override;
protected:
HRESULT scaleSwapChain(const SIZE &windowSize, const RECT &clientRect) override;
bool registerForSizeChangeEvents();
void unregisterForSizeChangeEvents();
HRESULT createSwapChain(ID3D11Device *device, DXGIFactory *factory, DXGI_FORMAT format, unsigned int width, unsigned int height, DXGISwapChain **swapChain);
HRESULT scaleSwapChain(const SIZE &newSize);
private:
ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> mSwapChainPanel;
@ -74,6 +77,6 @@ class SwapChainPanelSizeChangedHandler :
std::weak_ptr<InspectableNativeWindow> mHost;
};
HRESULT GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel, RECT *windowSize);
HRESULT GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel, SIZE *windowSize);
}
#endif // LIBANGLE_RENDERER_D3D_D3D11_WINRT_SWAPCHAINPANELNATIVEWINDOW_H_

Просмотреть файл

@ -269,38 +269,192 @@ HRESULT CreatePropertyMap(IMap<HSTRING, IInspectable*>** propertyMap)
return result;
}
HRESULT CreatePropertyValueStatics(IPropertyValueStatics** propertyStatics)
{
ComPtr<IPropertyValueStatics> propertyValueStatics;
HRESULT result = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propertyValueStatics);
EXPECT_HRESULT_SUCCEEDED(result);
result = propertyValueStatics.CopyTo(propertyStatics);
EXPECT_HRESULT_SUCCEEDED(result);
return result;
}
HRESULT SetInspectablePropertyValue(const ComPtr<IMap<HSTRING, IInspectable*>>& propertyMap, const wchar_t* propertyName, IInspectable* inspectable)
{
boolean propertyReplaced = false;
return propertyMap->Insert(HStringReference(propertyName).Get(), inspectable, &propertyReplaced);
}
TEST(NativeWindowTest, NativeWindowValidSwapChainPanel)
void expectNativeWindowInitCalls(MockSwapChainPanel &panel, bool expectRenderSize)
{
if (expectRenderSize)
{
EXPECT_CALL(panel, get_RenderSize(testing::_)).Times(1);
}
EXPECT_CALL(panel, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(panel, remove_SizeChanged(testing::_)).Times(1);
}
TEST(NativeWindowTest, SwapChainPanelByItself)
{
MockSwapChainPanel mockSwapChainPanel;
NativeWindow nativeWindow(reinterpret_cast<IInspectable*>(&mockSwapChainPanel));
EXPECT_CALL(mockSwapChainPanel, get_RenderSize(testing::_)).Times(1);
EXPECT_CALL(mockSwapChainPanel, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(mockSwapChainPanel, remove_SizeChanged(testing::_)).Times(1);
expectNativeWindowInitCalls(mockSwapChainPanel, true);
EXPECT_TRUE(nativeWindow.initialize());
}
TEST(NativeWindowTest, NativeWindowValidSwapChainPanelInPropertySet)
TEST(NativeWindowTest, SwapChainPanelInPropertySet)
{
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockSwapChainPanel mockSwapChainPanel;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
// Create a simple property set with the swapchainpanel
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockSwapChainPanel)));
// Check native window init calls
NativeWindow nativeWindow(propertySet.Get());
EXPECT_CALL(mockSwapChainPanel, get_RenderSize(testing::_)).Times(1);
EXPECT_CALL(mockSwapChainPanel, add_SizeChanged(testing::_, testing::_)).Times(1);
EXPECT_CALL(mockSwapChainPanel, remove_SizeChanged(testing::_)).Times(1);
expectNativeWindowInitCalls(mockSwapChainPanel, true);
EXPECT_TRUE(nativeWindow.initialize());
}
CoUninitialize();
}
TEST(NativeWindowTest, SwapChainPanelInPropertySetWithSizeAndScale)
{
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockSwapChainPanel mockSwapChainPanel;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
ComPtr<IPropertyValueStatics> propertyValueStatics;
ComPtr<IPropertyValue> singleValue;
ComPtr<IPropertyValue> sizeValue;
// Create a simple property set with the swapchainpanel
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockSwapChainPanel)));
// Add a valid scale factor to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSingle(0.5f, reinterpret_cast<IInspectable**>(singleValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderResolutionScaleProperty, reinterpret_cast<IInspectable*>(singleValue.Get())));
// Add a valid size to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSize({ 480, 800 }, reinterpret_cast<IInspectable**>(sizeValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderSurfaceSizeProperty, reinterpret_cast<IInspectable*>(sizeValue.Get())));
// Check native window init fails, since we shouldn't be able to set a size and a scale together
NativeWindow nativeWindow(propertySet.Get());
EXPECT_FALSE(nativeWindow.initialize());
}
CoUninitialize();
}
// Tests that the scale property works as expected in a property set with a SwapChainPanel
class SwapChainPanelScaleTest : public testing::TestWithParam<std::pair<float, bool>>
{
};
TEST_P(SwapChainPanelScaleTest, ValidateScale)
{
float scale = GetParam().first;
bool expectedResult = GetParam().second;
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockSwapChainPanel mockSwapChainPanel;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
ComPtr<IPropertyValueStatics> propertyValueStatics;
ComPtr<IPropertyValue> singleValue;
// Create a simple property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockSwapChainPanel)));
// Add a valid scale factor to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSingle(scale, reinterpret_cast<IInspectable**>(singleValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderResolutionScaleProperty, reinterpret_cast<IInspectable*>(singleValue.Get())));
// Check native window init status and calls to the mock swapchainpanel
NativeWindow nativeWindow(propertySet.Get());
if (expectedResult)
{
expectNativeWindowInitCalls(mockSwapChainPanel, true);
}
EXPECT_EQ(nativeWindow.initialize(), expectedResult);
}
CoUninitialize();
}
typedef std::pair<float, bool> scaleValidPair;
static const scaleValidPair scales[] = { scaleValidPair(1.0f, true),
scaleValidPair(0.5f, true),
scaleValidPair(0.0f, false),
scaleValidPair(0.01f, true),
scaleValidPair(2.00f, true) };
INSTANTIATE_TEST_CASE_P(NativeWindowTest,
SwapChainPanelScaleTest,
testing::ValuesIn(scales));
// Tests that the size property works as expected in a property set with a SwapChainPanel
class SwapChainPanelSizeTest : public testing::TestWithParam<std::tuple<float, float, bool>>
{
};
TEST_P(SwapChainPanelSizeTest, ValidateSize)
{
Size renderSize = { std::get<0>(GetParam()), std::get<1>(GetParam()) };
bool expectedResult = std::get<2>(GetParam());
// COM is required to be initialized for creation of the property set
EXPECT_HRESULT_SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
{
MockSwapChainPanel mockSwapChainPanel;
ComPtr<IMap<HSTRING, IInspectable*>> propertySet;
ComPtr<IPropertyValueStatics> propertyValueStatics;
ComPtr<IPropertyValue> sizeValue;
// Create a simple property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyMap(&propertySet));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLNativeWindowTypeProperty, reinterpret_cast<IInspectable*>(&mockSwapChainPanel)));
// Add a valid size to the property set
EXPECT_HRESULT_SUCCEEDED(CreatePropertyValueStatics(propertyValueStatics.GetAddressOf()));
propertyValueStatics->CreateSize(renderSize, reinterpret_cast<IInspectable**>(sizeValue.GetAddressOf()));
EXPECT_HRESULT_SUCCEEDED(SetInspectablePropertyValue(propertySet, EGLRenderSurfaceSizeProperty, reinterpret_cast<IInspectable*>(sizeValue.Get())));
// Check native window init status and calls to the mock swapchainpanel
NativeWindow nativeWindow(propertySet.Get());
if (expectedResult)
{
expectNativeWindowInitCalls(mockSwapChainPanel, false);
}
EXPECT_EQ(nativeWindow.initialize(), expectedResult);
}
CoUninitialize();
}
typedef std::tuple<float, float, bool> sizeValidPair;
static const sizeValidPair sizes[] = { sizeValidPair( 800, 480, true),
sizeValidPair( 0, 480, false),
sizeValidPair( 800, 0, false),
sizeValidPair( 0, 0, false) };
INSTANTIATE_TEST_CASE_P(NativeWindowTest,
SwapChainPanelSizeTest,
testing::ValuesIn(sizes));
} // namespace