diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml index 6b656d600..371913aa3 100644 --- a/winrt/docsrc/effects/PixelShaderEffect.xml +++ b/winrt/docsrc/effects/PixelShaderEffect.xml @@ -199,6 +199,39 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license + + Registers the shader code using the given effectId, which must be unique, and performs one-time reflection over the shader code to extract constant property names etc. Does nothing if the effectId is already registered. + +

+ Throws an exception if the reflection over the shader code fails. See for more detail about the shader code requirements. +

+

+ The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overridden on each instance. + The coordinateMappings, borderModes and sourceInterpolations parameters are optional and can be set to null or empty or partially specified. The values in the array apply to sources 1 to 8 in order. + These properties can be overridden on each instance. +

+
+
+ + Initializes a new instance of the PixelShaderEffect class using the shader code which was previously registered using the given effectId. + +

+ Throws an exception if not registered. +

+
+
+ + + Calls + then . + + + + Checks to see if the given effectId has been registered via . + + + Removes the cached shader code with the given effectId. Does nothing if not registered. + Gets or sets the first input source for the custom pixel shader. diff --git a/winrt/lib/effects/shader/ClipTransform.cpp b/winrt/lib/effects/shader/ClipTransform.cpp index 7187c6513..754159380 100644 --- a/winrt/lib/effects/shader/ClipTransform.cpp +++ b/winrt/lib/effects/shader/ClipTransform.cpp @@ -20,7 +20,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // PixelShaderTransform inputs, before any intermediate border transforms were applied to them. // This lets us compute a non infinite output rect even in the presence of hard borders. - return 1 + m_sharedState->Shader().InputCount; + return 1 + m_sharedState->Shader()->InputCount; } diff --git a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl index 83ed561bd..f90c6cc5b 100644 --- a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl +++ b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl @@ -14,6 +14,43 @@ namespace Microsoft.Graphics.Canvas.Effects Offset } SamplerCoordinateMapping; + [version(VERSION), uuid(39D6BA64-E00D-4E14-958C-F1329CE1011F), exclusiveto(PixelShaderEffect)] + interface IPixelShaderEffectStatics : IInspectable + { + HRESULT RegisterEffect( + [in] UINT32 shaderCodeCount, + [in, size_is(shaderCodeCount)] BYTE* shaderCode, + [in] GUID effectId, + [in] INT32 maxSamplerOffset, + [in] UINT32 coordinateMappingsSize, + [in, size_is(coordinateMappingsSize)] SamplerCoordinateMapping* coordinateMappings, + [in] UINT32 borderModesSize, + [in, size_is(borderModesSize)] EffectBorderMode* borderModes, + [in] UINT32 sourceInterpolationsSize, + [in, size_is(sourceInterpolationsSize)] Microsoft.Graphics.Canvas.CanvasImageInterpolation* sourceInterpolations); + + HRESULT CreateEffect( + [in] GUID effectId, + [out, retval] PixelShaderEffect** effect); + + HRESULT RegisterAndCreateEffect( + [in] UINT32 shaderCodeCount, + [in, size_is(shaderCodeCount)] BYTE* shaderCode, + [in] GUID effectId, + [in] INT32 maxSamplerOffset, + [in] UINT32 coordinateMappingsSize, + [in, size_is(coordinateMappingsSize)] SamplerCoordinateMapping* coordinateMappings, + [in] UINT32 borderModesSize, + [in, size_is(borderModesSize)] EffectBorderMode* borderModes, + [in] UINT32 sourceInterpolationsSize, + [in, size_is(sourceInterpolationsSize)] Microsoft.Graphics.Canvas.CanvasImageInterpolation* sourceInterpolations, + [out, retval] PixelShaderEffect** effect); + + HRESULT IsEffectRegistered([in] GUID effectId, [out, retval] boolean* result); + + HRESULT UnregisterEffect([in] GUID effectId); + }; + [version(VERSION), uuid(FC8C3C31-FA96-45E2-8B72-1741C65CEE8E), exclusiveto(PixelShaderEffect)] interface IPixelShaderEffect : IInspectable requires ICanvasEffect @@ -107,7 +144,7 @@ namespace Microsoft.Graphics.Canvas.Effects [out, retval] PixelShaderEffect** effect); }; - [version(VERSION), activatable(IPixelShaderEffectFactory, VERSION)] + [version(VERSION), activatable(IPixelShaderEffectFactory, VERSION), static(IPixelShaderEffectStatics, VERSION)] runtimeclass PixelShaderEffect { [default] interface IPixelShaderEffect; diff --git a/winrt/lib/effects/shader/PixelShaderEffect.cpp b/winrt/lib/effects/shader/PixelShaderEffect.cpp index 264287717..204bd94c9 100644 --- a/winrt/lib/effects/shader/PixelShaderEffect.cpp +++ b/winrt/lib/effects/shader/PixelShaderEffect.cpp @@ -5,7 +5,6 @@ #include "pch.h" #include "PixelShaderEffect.h" #include "PixelShaderEffectImpl.h" -#include "SharedShaderState.h" namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects { @@ -31,6 +30,107 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na }); } + IFACEMETHODIMP PixelShaderEffectFactory::RegisterEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset, + uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings, + uint32_t borderModesSize, EffectBorderMode* borderModes, + uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations) + { + return ExceptionBoundary([&] + { + if (effectId == GUID_NULL) { + ThrowHR(E_INVALIDARG); + } + + { + std::lock_guard lock(m_mutex); + auto const& cached = shaderCache.find(effectId); + if (cached != shaderCache.end()) { + return; + } + } + + CheckInPointer(shaderCode); + auto description = SharedShaderState::CreateShaderDescription(shaderCode, shaderCodeCount, effectId, maxSamplerOffset, + coordinateMappings, coordinateMappingsSize, borderModes, borderModesSize, sourceInterpolations, sourceInterpolationsSize); + + { + std::lock_guard lock(m_mutex); + auto const& cached = shaderCache.find(effectId); + if (cached != shaderCache.end()) { + return; + } + shaderCache.insert_or_assign(effectId, description); + } + }); + } + + IFACEMETHODIMP PixelShaderEffectFactory::CreateEffect(GUID effectId, IPixelShaderEffect** effect) + { + return ExceptionBoundary([&] + { + CheckAndClearOutPointer(effect); + + ShaderDescriptionWithDefaults description{}; + { + std::lock_guard lock(m_mutex); + auto const& cached = shaderCache.find(effectId); + if (cached != shaderCache.end()) { + description = cached->second; + } + } + + if (description.Description == nullptr || description.Defaults == nullptr) { + ThrowHR(E_INVALIDARG, Strings::CustomEffectNotRegistered); + } + + // Create a shared state object using the specified shader code. + auto sharedState = Make(description.Description, description.Defaults->DefaultConstants, description.Defaults->DefaultCoordinateMapping, description.Defaults->DefaultSourceInterpolation); + CheckMakeResult(sharedState); + + // Create the WinRT effect instance. + auto newEffect = Make(nullptr, nullptr, sharedState.Get()); + CheckMakeResult(newEffect); + + ThrowIfFailed(newEffect.CopyTo(effect)); + }); + } + + IFACEMETHODIMP PixelShaderEffectFactory::RegisterAndCreateEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset, + uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings, + uint32_t borderModesSize, EffectBorderMode* borderModes, + uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations, IPixelShaderEffect** effect) + { + return ExceptionBoundary([&] + { + ThrowIfFailed(RegisterEffect(shaderCodeCount, shaderCode, effectId, maxSamplerOffset, coordinateMappingsSize, coordinateMappings, borderModesSize, borderModes, sourceInterpolationsSize, sourceInterpolations)); + ThrowIfFailed(CreateEffect(effectId, effect)); + }); + } + + IFACEMETHODIMP PixelShaderEffectFactory::IsEffectRegistered(GUID effectId, boolean* result) + { + return ExceptionBoundary([&] + { + CheckInPointer(result); + + std::lock_guard lock(m_mutex); + auto const& cached = shaderCache.find(effectId); + *result = cached != shaderCache.end(); + }); + } + + IFACEMETHODIMP PixelShaderEffectFactory::UnregisterEffect(GUID effectId) + { + return ExceptionBoundary([&] + { + std::lock_guard lock(m_mutex); + shaderCache.erase(effectId); + }); + } + + std::recursive_mutex PixelShaderEffectFactory::m_mutex{}; + std::map PixelShaderEffectFactory::shaderCache{}; + // Describe how to implement WinRT IMap<> methods in terms of our shader constant buffer. template @@ -81,7 +181,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na PixelShaderEffect::PixelShaderEffect(ICanvasDevice* device, ID2D1Effect* effect, ISharedShaderState* sharedState) - : CanvasEffect(CLSID_PixelShaderEffect, 0, sharedState->Shader().InputCount, true, device, effect, static_cast(this)) + : CanvasEffect(CLSID_PixelShaderEffect, 0, sharedState->Shader()->InputCount, true, device, effect, static_cast(this)) , m_sharedState(sharedState) { m_propertyMap = Make(true, this); @@ -210,7 +310,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na ThrowIfFailed(As(device)->GetInterface(IID_PPV_ARGS(&d3dDevice))); auto deviceFeatureLevel = d3dDevice->GetFeatureLevel(); - auto shaderFeatureLevel = m_sharedState->Shader().MinFeatureLevel; + auto shaderFeatureLevel = m_sharedState->Shader()->MinFeatureLevel; return deviceFeatureLevel >= shaderFeatureLevel; } @@ -263,7 +363,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na IFACEMETHODIMP PixelShaderEffect::GetSource(unsigned int index, IGraphicsEffectSource** source) { - if (index >= m_sharedState->Shader().InputCount && source) + if (index >= m_sharedState->Shader()->InputCount && source) { // Getting an out of range source returns null, rather than throwing like most effects. *source = nullptr; @@ -279,14 +379,14 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na void PixelShaderEffect::SetSource(unsigned int index, IGraphicsEffectSource* source) { - if (index >= m_sharedState->Shader().InputCount) + if (index >= m_sharedState->Shader()->InputCount) { // Setting unused sources to null is valid. // If the value is not null, we format a nice exception message. if (source) { WinStringBuilder message; - message.Format(Strings::CustomEffectSourceOutOfRange, index + 1, m_sharedState->Shader().InputCount); + message.Format(Strings::CustomEffectSourceOutOfRange, index + 1, m_sharedState->Shader()->InputCount); ThrowHR(E_INVALIDARG, message.Get()); } } diff --git a/winrt/lib/effects/shader/PixelShaderEffect.h b/winrt/lib/effects/shader/PixelShaderEffect.h index 6760b8a9a..a4b712d90 100644 --- a/winrt/lib/effects/shader/PixelShaderEffect.h +++ b/winrt/lib/effects/shader/PixelShaderEffect.h @@ -4,6 +4,10 @@ #pragma once +#include "../../utils/GuidUtilities.h" +#include "ShaderDescription.h" +#include "SharedShaderState.h" + namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects { class ISharedShaderState; @@ -12,13 +16,39 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // WinRT activation factory. - class PixelShaderEffectFactory : public AgileActivationFactory + class PixelShaderEffectFactory : public AgileActivationFactory , private LifespanTracker { InspectableClassStatic(RuntimeClass_Microsoft_Graphics_Canvas_Effects_PixelShaderEffect, BaseTrust); public: + // + // IPixelShaderEffectFactory + // IFACEMETHOD(Create)(uint32_t shaderCodeCount, BYTE* shaderCode, IPixelShaderEffect** effect) override; + + // + // IPixelShaderEffectStatics + // + IFACEMETHOD(RegisterEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset, + uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings, + uint32_t borderModesSize, EffectBorderMode* borderModes, + uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations) override; + + IFACEMETHOD(CreateEffect)(GUID effectId, IPixelShaderEffect** effect) override; + + IFACEMETHOD(RegisterAndCreateEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset, + uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings, + uint32_t borderModesSize, EffectBorderMode* borderModes, + uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations, IPixelShaderEffect** effect) override; + + IFACEMETHOD(IsEffectRegistered)(GUID effectId, boolean* result) override; + + IFACEMETHOD(UnregisterEffect)(GUID effectId) override; + + private: + static std::recursive_mutex m_mutex; + static std::map shaderCache; }; diff --git a/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp b/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp index 507556d57..8f46cdaec 100644 --- a/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp +++ b/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp @@ -168,7 +168,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // Load our pixel shader into D2D. auto& shader = m_sharedState->Shader(); - HRESULT hr = m_effectContext->LoadPixelShader(shader.Hash, shader.Code.data(), static_cast(shader.Code.size())); + HRESULT hr = m_effectContext->LoadPixelShader(shader->Hash, shader->Code.data(), static_cast(shader->Code.size())); if (FAILED(hr)) ThrowHR(hr, Strings::CustomEffectBadShader); @@ -197,7 +197,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na ThrowIfFailed(m_transformGraph->ConnectNode(shaderTransform.Get(), clipTransform.Get(), 0)); ThrowIfFailed(m_transformGraph->SetOutputNode(clipTransform.Get())); - for (unsigned i = 0; i < m_sharedState->Shader().InputCount; i++) + for (unsigned i = 0; i < m_sharedState->Shader()->InputCount; i++) { switch (m_coordinateMapping->BorderMode[i]) { diff --git a/winrt/lib/effects/shader/PixelShaderTransform.cpp b/winrt/lib/effects/shader/PixelShaderTransform.cpp index 21a6aff10..db5a57f28 100644 --- a/winrt/lib/effects/shader/PixelShaderTransform.cpp +++ b/winrt/lib/effects/shader/PixelShaderTransform.cpp @@ -16,7 +16,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na IFACEMETHODIMP_(UINT32) PixelShaderTransform::GetInputCount() const { - return m_sharedState->Shader().InputCount; + return m_sharedState->Shader()->InputCount; } @@ -86,9 +86,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // Tell D2D to use our pixel shader. auto& shader = m_sharedState->Shader(); - ThrowIfFailed(drawInfo->SetPixelShader(shader.Hash)); + ThrowIfFailed(drawInfo->SetPixelShader(shader->Hash)); - drawInfo->SetInstructionCountHint(shader.InstructionCount); + drawInfo->SetInstructionCountHint(shader->InstructionCount); }); } @@ -101,7 +101,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na void PixelShaderTransform::SetSourceInterpolation(SourceInterpolationState const* sourceInterpolation) { - auto inputCount = m_sharedState->Shader().InputCount; + auto inputCount = m_sharedState->Shader()->InputCount; for (unsigned i = 0; i < inputCount; i++) { diff --git a/winrt/lib/effects/shader/SharedShaderState.cpp b/winrt/lib/effects/shader/SharedShaderState.cpp index e7afe53bb..1a3da7d99 100644 --- a/winrt/lib/effects/shader/SharedShaderState.cpp +++ b/winrt/lib/effects/shader/SharedShaderState.cpp @@ -28,7 +28,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na } - SharedShaderState::SharedShaderState(ShaderDescription const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation) + SharedShaderState::SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation) : m_shader(shader) , m_constants(constants) , m_coordinateMapping(coordinateMapping) @@ -38,16 +38,54 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na SharedShaderState::SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize) { - // Store the shader program code. - m_shader.Code.assign(shaderCode, shaderCode + shaderCodeSize); - - // Hash it to generate a unique ID. + // Hash the code to generate a unique ID. static const IID salt{ 0x489257f6, 0x6544, 0x4277, 0x89, 0x82, 0xea, 0xd1, 0x69, 0x39, 0x1f, 0x3d }; - m_shader.Hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize); + auto hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize); + + auto shaderWithDefaults = CreateShaderDescription(shaderCode, shaderCodeSize, hash, 0, nullptr, 0, nullptr, 0, nullptr, 0); + m_shader = shaderWithDefaults.Description; + m_constants.swap(shaderWithDefaults.Defaults->DefaultConstants); //Swap instead of copy since this may be relatively large and we are discarding shaderWithDefaults.Defaults at the end of this function + m_coordinateMapping = shaderWithDefaults.Defaults->DefaultCoordinateMapping; + m_sourceInterpolation = shaderWithDefaults.Defaults->DefaultSourceInterpolation; + } + + + ShaderDescriptionWithDefaults SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset, + SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize) + { + auto shader = ShaderDescriptionWithDefaults{}; + shader.Description = std::make_shared(); + shader.Defaults = std::make_shared(); + + // Store the shader program code. + shader.Description->Code.assign(shaderCode, shaderCode + shaderCodeSize); + + shader.Description->Hash = effectId; // Look up shader metadata. - ReflectOverShader(); + ReflectOverShader(shader); + shader.Defaults->DefaultCoordinateMapping.MaxOffset = maxSamplerOffset; + if (coordinateMappings != nullptr) { + for (uint32_t i = 0; i < coordinateMappingsSize && i < MaxShaderInputs; i++) { + shader.Defaults->DefaultCoordinateMapping.Mapping[i] = coordinateMappings[i]; + } + } + if (borderModes != nullptr) { + for (uint32_t i = 0; i < borderModesSize && i < MaxShaderInputs; i++) { + shader.Defaults->DefaultCoordinateMapping.BorderMode[i] = borderModes[i]; + } + } + if (sourceInterpolations != nullptr) { + for (uint32_t i = 0; i < sourceInterpolationsSize && i < MaxShaderInputs; i++) { + auto d2dFilter = ToD2DFilter(sourceInterpolations[i]); + if (d2dFilter != D2D1_FILTER_FORCE_DWORD) { + shader.Defaults->DefaultSourceInterpolation.Filter[i] = d2dFilter; + } + } + } + + return shader; } @@ -62,13 +100,13 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na unsigned SharedShaderState::GetPropertyCount() { - return static_cast(m_shader.Variables.size()); + return static_cast(m_shader->Variables.size()); } bool SharedShaderState::HasProperty(HSTRING name) { - return std::binary_search(m_shader.Variables.begin(), m_shader.Variables.end(), name, VariableNameComparison()); + return std::binary_search(m_shader->Variables.begin(), m_shader->Variables.end(), name, VariableNameComparison()); } @@ -210,9 +248,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na { std::vector properties; - properties.reserve(m_shader.Variables.size()); + properties.reserve(m_shader->Variables.size()); - for (auto& variable : m_shader.Variables) + for (auto& variable : m_shader->Variables) { properties.emplace_back(variable.Name, GetProperty(variable)); } @@ -225,9 +263,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na { VariableNameComparison comparison; - auto it = std::lower_bound(m_shader.Variables.begin(), m_shader.Variables.end(), name, comparison); + auto it = std::lower_bound(m_shader->Variables.begin(), m_shader->Variables.end(), name, comparison); - if (it == m_shader.Variables.end() || comparison(name, *it)) + if (it == m_shader->Variables.end() || comparison(name, *it)) { WinStringBuilder message; message.Format(Strings::CustomEffectUnknownProperty, WindowsGetStringRawBuffer(name, nullptr)); @@ -460,12 +498,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na } - void SharedShaderState::ReflectOverShader() + void SharedShaderState::ReflectOverShader(ShaderDescriptionWithDefaults const& output) { // Create the shader reflection interface. ComPtr reflector; - HRESULT hr = D3DReflect(m_shader.Code.data(), m_shader.Code.size(), IID_PPV_ARGS(&reflector)); + HRESULT hr = D3DReflect(output.Description->Code.data(), output.Description->Code.size(), IID_PPV_ARGS(&reflector)); if (FAILED(hr)) ThrowHR(E_INVALIDARG, Strings::CustomEffectBadShader); @@ -484,25 +522,25 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na } // Examine the input bindings. - ReflectOverBindings(reflector.Get(), desc); + ReflectOverBindings(output, reflector.Get(), desc); // Store the mapping from named constants to buffer locations. if (desc.ConstantBuffers) { - ReflectOverConstantBuffer(reflector->GetConstantBufferByIndex(0)); + ReflectOverConstantBuffer(output, reflector->GetConstantBufferByIndex(0)); } // Grab some other metadata. - m_shader.InstructionCount = desc.InstructionCount; + output.Description->InstructionCount = desc.InstructionCount; - ThrowIfFailed(reflector->GetMinFeatureLevel(&m_shader.MinFeatureLevel)); + ThrowIfFailed(reflector->GetMinFeatureLevel(&output.Description->MinFeatureLevel)); // If this shader was compiled to support shader linking, we can also determine which inputs are simple vs. complex. - ReflectOverShaderLinkingFunction(); + ReflectOverShaderLinkingFunction(output); } - void SharedShaderState::ReflectOverBindings(ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc) + void SharedShaderState::ReflectOverBindings(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc) { for (unsigned i = 0; i < desc.BoundResources; i++) { @@ -516,7 +554,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na ThrowHR(E_INVALIDARG, Strings::CustomEffectTooManyTextures); // Record how many input textures this shader uses. - m_shader.InputCount = std::max(m_shader.InputCount, inputDesc.BindPoint + 1); + output.Description->InputCount = std::max(output.Description->InputCount, inputDesc.BindPoint + 1); break; case D3D_SIT_CBUFFER: @@ -529,24 +567,24 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na } - void SharedShaderState::ReflectOverConstantBuffer(ID3D11ShaderReflectionConstantBuffer* constantBuffer) + void SharedShaderState::ReflectOverConstantBuffer(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionConstantBuffer* constantBuffer) { D3D11_SHADER_BUFFER_DESC desc; ThrowIfFailed(constantBuffer->GetDesc(&desc)); // Resize our constant buffer to match the shader. - m_constants.resize(desc.Size); + output.Defaults->DefaultConstants.resize(desc.Size); // Look up variable metadata. - m_shader.Variables.reserve(desc.Variables); + output.Description->Variables.reserve(desc.Variables); for (unsigned i = 0; i < desc.Variables; i++) { - ReflectOverVariable(constantBuffer->GetVariableByIndex(i)); + ReflectOverVariable(output, constantBuffer->GetVariableByIndex(i)); } // Sort the variables by name. - std::sort(m_shader.Variables.begin(), m_shader.Variables.end(), VariableNameComparison()); + std::sort(output.Description->Variables.begin(), output.Description->Variables.end(), VariableNameComparison()); } @@ -629,7 +667,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na } - void SharedShaderState::ReflectOverVariable(ID3D11ShaderReflectionVariable* variable) + void SharedShaderState::ReflectOverVariable(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionVariable* variable) { D3D11_SHADER_VARIABLE_DESC desc; ThrowIfFailed(variable->GetDesc(&desc)); @@ -652,7 +690,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // This can only fail if the shader blob is corrupted. auto endOffset = desc.StartOffset + desc.Size; - if (endOffset > m_constants.size() || endOffset < desc.StartOffset) + if (endOffset > output.Defaults->DefaultConstants.size() || endOffset < desc.StartOffset) { ThrowHR(E_UNEXPECTED); } @@ -660,15 +698,15 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // Initialize our constant buffer with the default value of the variable. if (desc.DefaultValue) { - CopyDefaultValue(m_constants.data() + desc.StartOffset, desc, type); + CopyDefaultValue(output.Defaults->DefaultConstants.data() + desc.StartOffset, desc, type); } // Store metadata about this variable. - m_shader.Variables.emplace_back(desc, type); + output.Description->Variables.emplace_back(desc, type); } - void SharedShaderState::ReflectOverShaderLinkingFunction() + void SharedShaderState::ReflectOverShaderLinkingFunction(ShaderDescriptionWithDefaults const& output) { // If this shader was compiled to support shader linking, we can get extra information // (telling us which inputs are simple vs. complex) from the shader linking function. @@ -676,7 +714,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na // It's valid to use shaders that don't support linking, so we return on failure rather than throwing. ComPtr privateData; - if (FAILED(D3DGetBlobPart(m_shader.Code.data(), m_shader.Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData))) + if (FAILED(D3DGetBlobPart(output.Description->Code.data(), output.Description->Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData))) return; ComPtr reflector; @@ -711,7 +749,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na else if (strstr(parameterDesc.SemanticName, "INPUT")) { // INPUT semantic means a simple input, so select passthrough coordinate mapping mode. - m_coordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne; + output.Defaults->DefaultCoordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne; } } } diff --git a/winrt/lib/effects/shader/SharedShaderState.h b/winrt/lib/effects/shader/SharedShaderState.h index d418870ce..48259643a 100644 --- a/winrt/lib/effects/shader/SharedShaderState.h +++ b/winrt/lib/effects/shader/SharedShaderState.h @@ -35,6 +35,23 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na }; + // Primarily used for caching + struct SharedShaderStateDefaults + { + std::vector DefaultConstants; + CoordinateMappingState DefaultCoordinateMapping; + SourceInterpolationState DefaultSourceInterpolation; + }; + + + // Primarily used for caching + struct ShaderDescriptionWithDefaults + { + std::shared_ptr Description; + std::shared_ptr Defaults; + }; + + // Implementation state shared between PixelShaderEffect and PixelShaderEffectImpl. // This stores the compiled shader code, metadata obtained via shader reflection, // and app-specified state such as the current constant buffer. @@ -51,7 +68,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na public: virtual ComPtr Clone() = 0; - virtual ShaderDescription const& Shader() = 0; + virtual std::shared_ptr const& Shader() = 0; virtual std::vector const& Constants() = 0; virtual CoordinateMappingState& CoordinateMapping() = 0; virtual SourceInterpolationState& SourceInterpolation() = 0; @@ -68,18 +85,20 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na class SharedShaderState : public RuntimeClass, ISharedShaderState> , private LifespanTracker { - ShaderDescription m_shader; + std::shared_ptr m_shader; std::vector m_constants; CoordinateMappingState m_coordinateMapping; SourceInterpolationState m_sourceInterpolation; public: - SharedShaderState(ShaderDescription const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation); + SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation); SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize); + static ShaderDescriptionWithDefaults SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset, + SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize); virtual ComPtr Clone() override; - virtual ShaderDescription const& Shader() override { return m_shader; } + virtual std::shared_ptr const& Shader() override { return m_shader; } virtual std::vector const& Constants() override { return m_constants; } virtual CoordinateMappingState& CoordinateMapping() override { return m_coordinateMapping; } virtual SourceInterpolationState& SourceInterpolation() { return m_sourceInterpolation; } @@ -107,12 +126,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na void CopyConstantData(ShaderVariable const& variable, TComponent* values); - // Shader reflection (done at init time). - void ReflectOverShader(); - void ReflectOverBindings(ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc); - void ReflectOverConstantBuffer(ID3D11ShaderReflectionConstantBuffer* constantBuffer); - void ReflectOverVariable(ID3D11ShaderReflectionVariable* variable); - void ReflectOverShaderLinkingFunction(); + // Shader reflection (done at init time). Note the members of the output must be non-null before calling these functions. + static void ReflectOverShader(ShaderDescriptionWithDefaults const& output); + static void ReflectOverBindings(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc); + static void ReflectOverConstantBuffer(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionConstantBuffer* constantBuffer); + static void ReflectOverVariable(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionVariable* variable); + static void ReflectOverShaderLinkingFunction(ShaderDescriptionWithDefaults const& output); }; }}}}} diff --git a/winrt/lib/text/CanvasFontFace.cpp b/winrt/lib/text/CanvasFontFace.cpp index 7e0726d99..39efe922b 100644 --- a/winrt/lib/text/CanvasFontFace.cpp +++ b/winrt/lib/text/CanvasFontFace.cpp @@ -6,7 +6,6 @@ #include "CanvasFontFace.h" #include "TextUtilities.h" -#include "effects/shader/PixelShaderEffect.h" #include "DrawGlyphRunHelper.h" using namespace ABI::Microsoft::Graphics::Canvas; diff --git a/winrt/lib/utils/Strings.inl b/winrt/lib/utils/Strings.inl index c03d5c5c6..2e5cb4826 100644 --- a/winrt/lib/utils/Strings.inl +++ b/winrt/lib/utils/Strings.inl @@ -37,6 +37,7 @@ STRING(CustomEffectUnknownProperty, L"Shader does not have a property named '%s' STRING(CustomEffectWrongPropertyArraySize, L"Wrong array size. Shader property '%s' is an array of %d elements.") STRING(CustomEffectWrongPropertyType, L"Wrong type. Shader property '%s' is of type %s.") STRING(CustomEffectWrongPropertyTypeArray, L"Wrong type. Shader property '%s' is an array of %s.") +STRING(CustomEffectNotRegistered, L"The effect is not registered.") STRING(DeviceExpectedToBeLost, L"This API was unexpectedly called when the Direct3D device is not lost.") STRING(DidNotPopLayer, L"After calling CanvasDrawingSession.CreateLayer, you must close the resulting CanvasActiveLayer before ending the CanvasDrawingSession.") STRING(DrawImageMinBlendNotSupported, L"This DrawImage overload is not valid when CanvasDrawingSession.Blend is set to CanvasBlend.Min.") diff --git a/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp b/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp index 27ba7d690..d8e26f376 100644 --- a/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp +++ b/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp @@ -19,7 +19,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; static ComPtr MakeSharedShaderState( - ShaderDescription const& shader = ShaderDescription(), + std::shared_ptr const& shader = std::make_shared(), std::vector const& constants = std::vector(), CoordinateMappingState const& coordinateMapping = CoordinateMappingState(), SourceInterpolationState const& sourceInterpolation = SourceInterpolationState()) @@ -148,7 +148,7 @@ TEST_CLASS(PixelShaderEffectUnitTests) sourceInterpolation.Filter[0] = D2D1_FILTER_MIN_MAG_MIP_POINT; sourceInterpolation.Filter[7] = D2D1_FILTER_ANISOTROPIC; - auto sharedState = MakeSharedShaderState(ShaderDescription(), constants, coordinateMapping, sourceInterpolation); + auto sharedState = MakeSharedShaderState(std::make_shared(), constants, coordinateMapping, sourceInterpolation); auto effect = Make(nullptr, nullptr, sharedState.Get()); // Realize the effect. @@ -181,8 +181,8 @@ TEST_CLASS(PixelShaderEffectUnitTests) ShaderVariable variable(variableDesc, variableType); - ShaderDescription desc; - desc.Variables.push_back(variable); + std::shared_ptr desc = std::make_shared(); + desc->Variables.push_back(variable); auto sharedState = MakeSharedShaderState(desc); @@ -202,7 +202,7 @@ TEST_CLASS(PixelShaderEffectUnitTests) *reinterpret_cast(data) = CLSID_PixelShaderEffect; break; - case PixelShaderEffectProperty::SharedState: + case ((uint32_t)PixelShaderEffectProperty::SharedState): Assert::AreEqual(sizeof(IUnknown*), dataSize); *reinterpret_cast(data) = As(sharedState).Detach(); break; @@ -244,8 +244,8 @@ TEST_CLASS(PixelShaderEffectUnitTests) ShaderVariable variable(variableDesc, variableType); - ShaderDescription desc; - desc.Variables.push_back(variable); + std::shared_ptr desc = std::make_shared(); + desc->Variables.push_back(variable); auto sharedState = MakeSharedShaderState(desc, std::vector(sizeof(int))); auto effect = Make(nullptr, nullptr, sharedState.Get()); @@ -575,10 +575,10 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) Assert::AreEqual(E_FAIL, impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE)); // Set the shared state property. - ShaderDescription desc; - desc.Code = { 2, 5, 7, 9, 4 }; - desc.Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 }; - desc.InputCount = 7; + std::shared_ptr desc = std::make_shared(); + desc->Code = { 2, 5, 7, 9, 4 }; + desc->Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 }; + desc->InputCount = 7; auto sharedState = MakeSharedShaderState(desc); @@ -587,18 +587,18 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) // The first PrepareForRender should call LoadPixelShader and configure the transform graph. f.MockEffectContext->LoadPixelShaderMethod.SetExpectedCalls(1, [&](IID const& shaderId, BYTE const* shaderBuffer, unsigned shaderBufferCount) { - Assert::AreEqual(desc.Hash, shaderId); - Assert::AreEqual(desc.Code.size(), shaderBufferCount); + Assert::AreEqual(desc->Hash, shaderId); + Assert::AreEqual(desc->Code.size(), shaderBufferCount); for (unsigned i = 0; i < shaderBufferCount; i++) { - Assert::AreEqual(desc.Code[i], shaderBuffer[i]); + Assert::AreEqual(desc->Code[i], shaderBuffer[i]); } return S_OK; }); - f.ExpectTransformGraph(desc.InputCount, 0); + f.ExpectTransformGraph(desc->InputCount, 0); ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE)); @@ -732,8 +732,8 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) PixelShaderEffectImpl::Register(f.Factory.Get()); auto& binding = f.FindBinding(L"CoordinateMapping"); - ShaderDescription desc; - desc.InputCount = 2; + std::shared_ptr desc = std::make_shared(); + desc->InputCount = 2; auto impl = Make(); @@ -805,7 +805,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) // PrepareForRender should create a graph with no intermediate border transforms. GraphState graphState; - f.ExpectTransformGraph(desc.InputCount, 0, mockDrawInfo.Get(), &graphState); + f.ExpectTransformGraph(desc->InputCount, 0, mockDrawInfo.Get(), &graphState); ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE)); @@ -847,7 +847,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) ThrowIfFailed(binding.setFunction(impl.Get(), reinterpret_cast(&value), sizeof(CoordinateMappingState))); - f.ExpectTransformGraph(desc.InputCount, 1, mockDrawInfo.Get(), &graphState); + f.ExpectTransformGraph(desc->InputCount, 1, mockDrawInfo.Get(), &graphState); ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE)); @@ -888,8 +888,8 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) auto impl = Make(); - ShaderDescription desc; - desc.InputCount = 2; + std::shared_ptr desc = std::make_shared(); + desc->InputCount = 2; auto sharedState = MakeSharedShaderState(desc); ThrowIfFailed(f.FindBinding(L"SharedState").setFunction(impl.Get(), reinterpret_cast(sharedState.GetAddressOf()), sizeof(ISharedShaderState*))); @@ -936,7 +936,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests) // PrepareForRender should pass the interpolation mode through to SetInputDescription. auto mockDrawInfo = Make(); - f.ExpectTransformGraph(desc.InputCount, 0, mockDrawInfo.Get()); + f.ExpectTransformGraph(desc->InputCount, 0, mockDrawInfo.Get()); f.MockEffectContext->LoadPixelShaderMethod.SetExpectedCalls(1); @@ -979,8 +979,8 @@ TEST_CLASS(PixelShaderTransformUnitTests) public: TEST_METHOD_EX(PixelShaderTransform_GetInputCount) { - ShaderDescription desc; - desc.InputCount = 5; + std::shared_ptr desc = std::make_shared(); + desc->InputCount = 5; auto sharedState = MakeSharedShaderState(desc); auto transform = Make(sharedState.Get(), std::make_shared()); @@ -991,9 +991,9 @@ TEST_CLASS(PixelShaderTransformUnitTests) TEST_METHOD_EX(PixelShaderTransform_SetDrawInfo) { - ShaderDescription desc; - desc.InstructionCount = 123; - desc.Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 }; + std::shared_ptr desc = std::make_shared(); + desc->InstructionCount = 123; + desc->Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 }; auto sharedState = MakeSharedShaderState(desc); auto transform = Make(sharedState.Get(), std::make_shared()); @@ -1003,14 +1003,14 @@ TEST_CLASS(PixelShaderTransformUnitTests) // SetDrawInfo should call SetPixelShader and SetInstructionCountHint. mockDrawInfo->SetPixelShaderMethod.SetExpectedCalls(1, [&](IID const& iid, D2D1_PIXEL_OPTIONS options) { - Assert::AreEqual(desc.Hash, iid); + Assert::AreEqual(desc->Hash, iid); Assert::AreEqual(D2D1_PIXEL_OPTIONS_NONE, options); return S_OK; }); mockDrawInfo->SetInstructionCountHintMethod.SetExpectedCalls(1, [&](UINT32 instructionCount) { - Assert::AreEqual(desc.InstructionCount, instructionCount); + Assert::AreEqual(desc->InstructionCount, instructionCount); }); ThrowIfFailed(transform->SetDrawInfo(mockDrawInfo.Get())); @@ -1099,8 +1099,8 @@ TEST_CLASS(ClipTransformUnitTests) public: TEST_METHOD_EX(ClipTransform_GetInputCount) { - ShaderDescription desc; - desc.InputCount = 5; + std::shared_ptr desc = std::make_shared(); + desc->InputCount = 5; auto sharedState = MakeSharedShaderState(desc); auto transform = Make(sharedState.Get(), std::make_shared()); @@ -1391,8 +1391,8 @@ TEST_CLASS(SharedShaderStateUnitTests) TEST_METHOD_EX(SharedShaderState_Clone) { - ShaderDescription desc; - desc.InputCount = 73; + std::shared_ptr desc = std::make_shared(); + desc->InputCount = 73; std::vector constants = { 29 }; @@ -1404,7 +1404,7 @@ TEST_CLASS(SharedShaderStateUnitTests) auto originalState = MakeSharedShaderState(desc, constants, coordinateMapping, sourceInterpolation); - Assert::AreEqual(desc.InputCount, originalState->Shader().InputCount); + Assert::AreEqual(desc->InputCount, originalState->Shader()->InputCount); Assert::AreEqual(1, originalState->Constants().size()); Assert::AreEqual(constants[0], originalState->Constants()[0]); Assert::AreEqual(coordinateMapping.MaxOffset, originalState->CoordinateMapping().MaxOffset); @@ -1412,7 +1412,7 @@ TEST_CLASS(SharedShaderStateUnitTests) auto clone = originalState->Clone(); - Assert::AreEqual(desc.InputCount, clone->Shader().InputCount); + Assert::AreEqual(desc->InputCount, clone->Shader()->InputCount); Assert::AreEqual(1, clone->Constants().size()); Assert::AreEqual(constants[0], clone->Constants()[0]); Assert::AreEqual(coordinateMapping.MaxOffset, clone->CoordinateMapping().MaxOffset); @@ -1433,10 +1433,10 @@ TEST_CLASS(SharedShaderStateUnitTests) auto state2a = Make(compiledShader2.data(), static_cast(compiledShader2.size())); auto state2b = Make(compiledShader2.data(), static_cast(compiledShader2.size())); - Assert::AreEqual(state1a->Shader().Hash, state1b->Shader().Hash); - Assert::AreEqual(state2a->Shader().Hash, state2b->Shader().Hash); + Assert::AreEqual(state1a->Shader()->Hash, state1b->Shader()->Hash); + Assert::AreEqual(state2a->Shader()->Hash, state2b->Shader()->Hash); - Assert::AreNotEqual(state1a->Shader().Hash, state2a->Shader().Hash); + Assert::AreNotEqual(state1a->Shader()->Hash, state2a->Shader()->Hash); }; @@ -1444,12 +1444,12 @@ TEST_CLASS(SharedShaderStateUnitTests) { auto state = Make(compiledShader1.data(), static_cast(compiledShader1.size())); - Assert::AreEqual(compiledShader1, state->Shader().Code); - Assert::AreEqual(0u, state->Shader().InputCount); - Assert::IsTrue(state->Shader().InstructionCount > 3 && state->Shader().InstructionCount < 20); - Assert::AreEqual(D3D_FEATURE_LEVEL_10_0, state->Shader().MinFeatureLevel); + Assert::AreEqual(compiledShader1, state->Shader()->Code); + Assert::AreEqual(0u, state->Shader()->InputCount); + Assert::IsTrue(state->Shader()->InstructionCount > 3 && state->Shader()->InstructionCount < 20); + Assert::AreEqual(D3D_FEATURE_LEVEL_10_0, state->Shader()->MinFeatureLevel); - auto& variables = state->Shader().Variables; + auto& variables = state->Shader()->Variables; Assert::AreEqual(7, variables.size()); diff --git a/winrt/test.managed/PixelShaderEffectTests.cs b/winrt/test.managed/PixelShaderEffectTests.cs index f4daec61a..2f88a36de 100644 --- a/winrt/test.managed/PixelShaderEffectTests.cs +++ b/winrt/test.managed/PixelShaderEffectTests.cs @@ -10,6 +10,9 @@ using System.Linq; using System.Runtime.InteropServices; using Windows.Foundation; +using System.Xml.Linq; +using Windows.UI.Xaml.Controls; + #if WINDOWS_UWP using System.Numerics; @@ -318,6 +321,38 @@ float4 main() : SV_Target } } + [TestMethod] + public void PixelShaderEffect_RegisterAndCreateEffect() { + const string hlsl = + @" + float4 color; + + float4 main() : SV_Target + { + return color; + } + "; + + var effectId = Guid.Parse("A686195C-AEA4-45B7-87EF-BDD57776A7F4"); + var shaderBytes = ShaderCompiler.CompileShader(hlsl, "ps_4_0"); + var effect = PixelShaderEffect.RegisterAndCreateEffect(shaderBytes, effectId, 0, null, null, null); + + using (var canvasDevice = new CanvasDevice()) + using (var renderTarget = new CanvasRenderTarget(canvasDevice, 1, 1, 96)) + using (var drawingSession = renderTarget.CreateDrawingSession()) { + drawingSession.DrawImage(effect); + } + + Assert.IsTrue(PixelShaderEffect.IsEffectRegistered(effectId)); + PixelShaderEffect.UnregisterEffect(effectId); + Assert.IsFalse(PixelShaderEffect.IsEffectRegistered(effectId)); + PixelShaderEffect.RegisterEffect(shaderBytes, effectId, 1, new SamplerCoordinateMapping[] { SamplerCoordinateMapping.Offset }, new EffectBorderMode[] { EffectBorderMode.Hard }, new CanvasImageInterpolation[] { CanvasImageInterpolation.NearestNeighbor }); + effect = PixelShaderEffect.CreateEffect(effectId); + Assert.AreEqual(effect.MaxSamplerOffset, 1); + Assert.AreEqual(effect.Source1Mapping, SamplerCoordinateMapping.Offset); + Assert.AreEqual(effect.Source1BorderMode, EffectBorderMode.Hard); + Assert.AreEqual(effect.Source1Interpolation, CanvasImageInterpolation.NearestNeighbor); + } [TestMethod] public void PixelShaderEffect_PropertiesDictionary_Methods()